diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookDTO.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookDTO.java new file mode 100644 index 0000000..6e75dbb --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookDTO.java @@ -0,0 +1,23 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book; + +import lombok.Builder; +import lombok.Getter; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Date; + +@Builder +@Getter +public class BookDTO { + private final String isbn; + private final String title; + private final String author; + private final String publisher; + private final Date date; + private final double price; + private final int initialStock; + private final ArrayList categories; + private final String description; + private final String language; +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookInfo.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookInfo.java new file mode 100644 index 0000000..266622d --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/BookInfo.java @@ -0,0 +1,17 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book; + +import java.util.ArrayList; +import java.util.Date; + +public record BookInfo(String isbn, + String title, + String author, + String publisher, + Date date, + double price, + int initialStock, + ArrayListcategories, + String description, + String language){ + +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverter.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverter.java new file mode 100644 index 0000000..664ab65 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverter.java @@ -0,0 +1,44 @@ +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 fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerDTO; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer; + +public final class BookConverter { + private BookConverter(){ + + } + + public static Book toDomain(BookInfo newBook) { + return Book.builder() + .isbn(newBook.isbn()) + .title(newBook.title()) + .author(newBook.author()) + .publisher(newBook.publisher()) + .date(newBook.date()) + .price(newBook.price()) + .initialStock(newBook.initialStock()) + .categories(newBook.categories()) + .description(newBook.description()) + .language(newBook.language()) + .build(); + } + + public static BookDTO toDTO(Book book) { + return BookDTO.builder() + .isbn(book.getIsbn()) + .title(book.getTitle()) + .author(book.getAuthor()) + .publisher(book.getPublisher()) + .date(book.getDate()) + .price(book.getPrice()) + .initialStock(book.getInitialStock()) + .categories(book.getCategories()) + .description(book.getDescription()) + .language(book.getLanguage()) + .build(); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/Book.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/Book.java new file mode 100644 index 0000000..bfc81bb --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/Book.java @@ -0,0 +1,37 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.entity; + + + + +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.IllegalBookStockException; +import lombok.Builder; +import lombok.Getter; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Date; + +@Builder +@Getter +public class Book { + + private String isbn; + private String title; + private String author; + private String publisher; + private Date date; + private double price; + private int initialStock; + private ArrayList categories; + private String description; + private String language; + + public void removeStock(int stockToRemove) throws IllegalBookStockException{ + if (initialStock - stockToRemove <0){ throw new IllegalBookStockException(stockToRemove,initialStock); } + initialStock = initialStock - stockToRemove; + } + + public void addStock(int stockToAdd) { + initialStock = initialStock + stockToAdd; + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundException.java new file mode 100644 index 0000000..9a517e5 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundException.java @@ -0,0 +1,13 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.exception; + +import java.text.MessageFormat; +import java.util.UUID; + +public class BookNotFoundException extends Exception { + + public static final String THE_BOOK_WITH_ISBN_DOES_NOT_EXIST_MESSAGE = "The book with ISBN {0} does not exist"; + + public BookNotFoundException(String isbn) { + super(MessageFormat.format(THE_BOOK_WITH_ISBN_DOES_NOT_EXIST_MESSAGE, isbn)); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/IllegalBookStockException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/IllegalBookStockException.java new file mode 100644 index 0000000..4d5dc52 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/IllegalBookStockException.java @@ -0,0 +1,13 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.exception; + +import java.text.MessageFormat; + +public class IllegalBookStockException extends Exception { + + public static final String CANNOT_REMOVE_STOCK = "Cannot remove {0} stock from {1} points"; + + public IllegalBookStockException(int needed, int actual) { + super(MessageFormat.format(CANNOT_REMOVE_STOCK, needed, + actual)); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookException.java new file mode 100644 index 0000000..015124e --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookException.java @@ -0,0 +1,8 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.exception; + +public class NotValidBookException extends Exception { + + public NotValidBookException(String message) { + super(message); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepository.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepository.java new file mode 100644 index 0000000..a45d12d --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepository.java @@ -0,0 +1,46 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.repository; + +import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +public final class BookRepository { + private final List books = new ArrayList<>(); + + public List findAll() { + return books; + } + + public void deleteAll() { + books.clear(); + } + + public Book save(Book newBook) { + Optional optionalBookWithSameId = this.findByISBN(newBook.getIsbn()); + optionalBookWithSameId.ifPresent(books::remove); + this.books.add(newBook); + return newBook; + } + + public Optional findByISBN(String isbn) { + + return this.books.stream() + .filter(book -> book.getIsbn().equals(isbn)) + .findFirst(); + } + + public boolean existsByISBN(String isbn) { + return this.books.stream() + .anyMatch(book -> book.getIsbn().equals(isbn)); + } + + + + public void delete(Book book) { + this.books.remove(book); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCase.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCase.java new file mode 100644 index 0000000..b19b31c --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCase.java @@ -0,0 +1,84 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.usecase; + +import fr.iut_fbleau.but3.dev62.mylibrary.book.BookDTO; +import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.book.converter.BookConverter; +import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.BookNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.IllegalBookStockException; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException; +import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository; +import fr.iut_fbleau.but3.dev62.mylibrary.book.validator.BookValidator; +import java.util.Optional; +import java.util.UUID; + +public final class BookUseCase { + + private final BookRepository bookRepository; + + public BookUseCase(BookRepository bookRepository) { + this.bookRepository = bookRepository; + } + + public String registerBook(BookInfo newBook) throws NotValidBookException { + BookValidator.validate(newBook); + Book bookToRegister = BookConverter.toDomain(newBook); + Book bookToRegistered = bookRepository.save(bookToRegister); + return bookToRegistered.getIsbn(); + } + + public Optional findBookByISBN(String isbn) { + Optional optionalBook = bookRepository.findByISBN(isbn); + return optionalBook.map(BookConverter::toDTO); + } + + public BookDTO updateBook(String isbn, BookInfo bookInfo) + throws BookNotFoundException, NotValidBookException { + BookValidator.validate(bookInfo); + Book bookByISBN = getBookIfDoesNotExistThrowBookNotFoundException( + isbn); + Book book = Book.builder() + .isbn(isbn) + .title(bookInfo.title()) + .author(bookInfo.author()) + .publisher(bookInfo.publisher()) + .date(bookInfo.date()) + .price(bookInfo.price()) + .initialStock(bookInfo.initialStock()) + .categories(bookInfo.categories()) + .description(bookInfo.description()) + .language(bookInfo.language()) + .build(); + Book updatedBook = bookRepository.save(book); + return BookConverter.toDTO(updatedBook); + } + + public void deleteBook(String isbn) throws BookNotFoundException { + Book bookToDelete = getBookIfDoesNotExistThrowBookNotFoundException(isbn); + this.bookRepository.delete(bookToDelete); + } + + public int addStock(String isbn, int stockToAdd) throws BookNotFoundException { + Book bookToAddStock = getBookIfDoesNotExistThrowBookNotFoundException(isbn); + bookToAddStock.addStock(stockToAdd); + bookRepository.save(bookToAddStock); + return bookToAddStock.getInitialStock(); + } + + public int subtractStock(String isbn, int loyaltyPointToRemove) + throws BookNotFoundException, IllegalBookStockException { + Book bookToSubtractStock = getBookIfDoesNotExistThrowBookNotFoundException(isbn); + bookToSubtractStock.removeStock(loyaltyPointToRemove); + bookRepository.save(bookToSubtractStock); + return bookToSubtractStock.getInitialStock(); + } + + private Book getBookIfDoesNotExistThrowBookNotFoundException(String isbn) + throws BookNotFoundException { + Optional optionalBookById = bookRepository.findByISBN(isbn); + if (optionalBookById.isEmpty()) { + throw new BookNotFoundException(isbn); + } + return optionalBookById.get(); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidator.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidator.java new file mode 100644 index 0000000..b6f30f3 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidator.java @@ -0,0 +1,71 @@ +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.BookInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException; + +public final class BookValidator { + + public static final String ISBN_IS_NOT_VALID = "ISBN is not valid"; + public static final String ISBN_CANNOT_BE_BLANK = "ISBN cannot be blank"; + public static final String TITLE_CANNOT_BE_BLANK = "Title cannot be blank"; + 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 PRICE_CANNOT_BE_NEGATIVE = "Price cannot be negative"; + public static final String PRICE_CANNOT_BE_EQUAL_TO_ZERO = "Price cannot be equal to zero"; + public static final String INITIAL_STOCK_CANNOT_BE_NEGATIVE = "Stock cannot be negative"; + + public static final String ISBN_REGEX = "\\d{13}"; + + private BookValidator() { + + } + + public static void validate(BookInfo newBook) throws NotValidBookException { + validateAuthor(newBook); + validateTitle(newBook); + validateISBN(newBook); + } + + private static void validateISBN(BookInfo newBook) + throws NotValidBookException { + if (newBook.isbn().isBlank()) { + throw new NotValidBookException(ISBN_CANNOT_BE_BLANK); + } + if (!newBook.isbn().matches(ISBN_REGEX)) { + throw new NotValidBookException(ISBN_IS_NOT_VALID); + } + } + + private static void validateTitle(BookInfo newBook) throws NotValidBookException { + if (newBook.title().isBlank()) { + throw new NotValidBookException(TITLE_CANNOT_BE_BLANK); + } + } + + private static void validateAuthor(BookInfo newBook) throws NotValidBookException { + if (newBook.author().isBlank()) { + throw new NotValidBookException(AUTHOR_CANNOT_BE_BLANK); + } + } + + private static void validatePublisher(BookInfo newBook) throws NotValidBookException { + if (newBook.publisher().isBlank()){ + throw new NotValidBookException(PUBLISHER_CANNOT_BE_BLANK); + } + } + + private static void validatePrice(BookInfo newBook) throws NotValidBookException { + if (newBook.price() < 0){ + throw new NotValidBookException(PRICE_CANNOT_BE_NEGATIVE); + } else if (newBook.price() == 0){ + throw new NotValidBookException(PRICE_CANNOT_BE_EQUAL_TO_ZERO); + } + } + + private static void validateInitialStock(BookInfo newBook) throws NotValidBookException { + if (newBook.initialStock() < 0){ + throw new NotValidBookException(INITIAL_STOCK_CANNOT_BE_NEGATIVE); + } + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/Customer.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/Customer.java index 5a2fa69..edeb599 100644 --- a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/Customer.java +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/Customer.java @@ -8,6 +8,7 @@ import lombok.Getter; @Builder @Getter + public class Customer { private UUID id; private String firstName; diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverterTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverterTest.java new file mode 100644 index 0000000..6999dd6 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/converter/BookConverterTest.java @@ -0,0 +1,138 @@ +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.converter.BookConverter; +import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertNull; + +@DisplayName("BookConverter Unit Tests") +public class BookConverterTest { + + @Nested + @DisplayName("toDomain() method tests") + class ToDomainTests { + + @Test + @DisplayName("Should convert BookInfo to Book domain object") + void shouldConvertBookInfoToDomain() { + // Given + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookInfo = new BookInfo("1234567891234", "Livre", "john doe","editeur", Date.from(Instant.now()),5.99,15,categories,"description","langue"); + + // When + Book result = BookConverter.toDomain(bookInfo); + + // Then + assertNotNull(result); + assertEquals(bookInfo.isbn(), result.getIsbn()); + assertEquals(bookInfo.title(), result.getTitle()); + assertEquals(bookInfo.author(), result.getAuthor()); + assertEquals(bookInfo.publisher(), result.getPublisher()); + assertEquals(bookInfo.date(), result.getDate()); + assertEquals(bookInfo.price(), result.getPrice()); + assertEquals(bookInfo.initialStock(), result.getInitialStock()); + assertEquals(bookInfo.categories(), result.getCategories()); + assertEquals(bookInfo.description(), result.getDescription()); + assertEquals(bookInfo.language(), result.getLanguage()); + } + } + @Nested + @DisplayName("toDTO() method tests") + class ToDTOTests { + + @Test + @DisplayName("Should convert Book domain object to BookDTO with all fields mapped correctly") + void shouldConvertBookToDTO() { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + Book book = Book.builder() + .isbn("1234567890123") + .title("Livre 1") + .author("John Updated") + .publisher("Editeur") + .date(Date.from(Instant.now())) + .price(4.99) + .initialStock(42) + .categories(categories) + .description("Description") + .language("Francais") + .build(); + + BookDTO result = BookConverter.toDTO(book); + + assertNotNull(result); + assertEquals(book.getIsbn(), result.getIsbn()); + assertEquals(book.getTitle(), result.getTitle()); + assertEquals(book.getAuthor(), result.getAuthor()); + assertEquals(book.getPublisher(), result.getPublisher()); + assertEquals(book.getDate(), result.getDate()); + assertEquals(book.getPrice(), result.getPrice()); + assertEquals(book.getInitialStock(), result.getInitialStock()); + assertEquals(book.getCategories(), result.getCategories()); + assertEquals(book.getDescription(), result.getDescription()); + assertEquals(book.getLanguage(), result.getLanguage()); + } + } + + @Test + @DisplayName("Should handle null values properly when converting between objects") + void shouldHandleNullValuesGracefully() { + Book book = Book.builder() + .isbn("1234567890123") + .title("Null") + .author("Null") + .publisher("Null") + .date(null) + .price(4.99) + .initialStock(42) + .categories(null) + .description(null) + .language(null) + .build(); + + BookDTO result = BookConverter.toDTO(book); + + assertNotNull(result); + assertEquals(book.getIsbn(), result.getIsbn()); + assertEquals(book.getTitle(), result.getTitle()); + assertEquals(book.getAuthor(), result.getAuthor()); + assertEquals(book.getPublisher(), result.getPublisher()); + assertNull(result.getDate()); + assertEquals(book.getPrice(), result.getPrice()); + assertEquals(book.getInitialStock(), result.getInitialStock()); + assertNull(result.getCategories()); + assertNull(result.getDescription()); + assertNull(result.getLanguage()); + } + + @Test + @DisplayName("Should preserve empty string values during conversion") + void shouldPreserveEmptyStrings() { + BookInfo bookInfo = new BookInfo("1234567890123", "e", "e","e",null,1.2,5,null,"",""); + + Book domainResult = BookConverter.toDomain(bookInfo); + BookDTO result = BookConverter.toDTO(domainResult); + + + assertEquals(bookInfo.isbn(), result.getIsbn()); //ISBN ne peut pas etre vide + assertEquals(bookInfo.title(), result.getTitle()); // Le titre ne peut pas etre vide + assertEquals(bookInfo.author(), result.getAuthor()); // L'auteur ne peut pas etre vide + assertEquals(bookInfo.publisher(), result.getPublisher()); // L'editeur ne peut pas etre vide + assertEquals(null, result.getDate()); + assertEquals(bookInfo.price(), result.getPrice()); + assertEquals(bookInfo.initialStock(), result.getInitialStock()); + assertEquals(null, result.getCategories()); + assertEquals("", result.getDescription()); + assertEquals("", result.getLanguage()); + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/BookTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/BookTest.java new file mode 100644 index 0000000..7bf1368 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/entity/BookTest.java @@ -0,0 +1,232 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.entity; + +import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.IllegalBookStockException; +import io.cucumber.java.bs.A; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import static org.junit.jupiter.api.Assertions.*; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.UUID; + +class BookTest { + + @Test + @DisplayName("Builder should create a valid Book instance") + void testBookBuilder() { + String ISBN = "1234567890123"; + String title = "LivreRandom"; + String author = "John Doe"; + String publisher = "EditeurRandom"; + Date date = Date.from(Instant.now()); + double price = 5.99; + int initialStock = 15; + ArrayList categories = new ArrayList<>(); categories.add("Histoire"); categories.add("Action"); + String description = "Je suis un livre qui contient des phrases composees de mots qui sont eux memes composes de lettres faits a partir d'encre sur du papier relie par une couverture cartonnee"; + String language = "Francais"; + + Book book = Book.builder() + .isbn(ISBN) + .title(title) + .author(author) + .publisher(publisher) + .date(date) + .price(price) + .initialStock(initialStock) + .categories(categories) + .description(description) + .language(language) + .build(); + + assertEquals(ISBN, book.getIsbn()); + assertEquals(title, book.getTitle()); + assertEquals(author, book.getAuthor()); + assertEquals(publisher, book.getPublisher()); + assertEquals(date, book.getDate()); + assertEquals(price, book.getPrice()); + assertEquals(initialStock, book.getInitialStock()); + assertEquals(categories, book.getCategories()); + assertEquals(description, book.getDescription()); + assertEquals(language, book.getLanguage()); + } + + + @Nested + @DisplayName("Book stock Tests") + class BookStockTests { + + @Test + @DisplayName("addStock should correctly increment stock") + void testAddStock() { + + String ISBN = "1234567890123"; + String title = "LivreRandom"; + String author = "John Doe"; + String publisher = "EditeurRandom"; + Date date = Date.from(Instant.now()); + double price = 5.99; + int initialStock = 50; + ArrayList categories = new ArrayList<>(); categories.add("Histoire"); categories.add("Action"); + String description = "Je suis un livre qui contient des phrases composees de mots qui sont eux memes composes de lettres faits a partir d'encre sur du papier relie par une couverture cartonnee"; + String language = "Francais"; + Book book = Book.builder() + .isbn(ISBN) + .title(title) + .author(author) + .publisher(publisher) + .date(date) + .price(price) + .initialStock(initialStock) + .categories(categories) + .description(description) + .language(language) + .build(); + int stockToAdd = 25; + int expectedStock = 75; + + book.addStock(stockToAdd); + + assertEquals(expectedStock, book.getInitialStock()); + } + + @Test + @DisplayName("addStock should handle zero points correctly") + void testAddZeroStock() { + String ISBN = "1234567890123"; + String title = "LivreRandom"; + String author = "John Doe"; + String publisher = "EditeurRandom"; + Date date = Date.from(Instant.now()); + double price = 5.99; + int initialStock = 50; + ArrayList categories = new ArrayList<>(); categories.add("Histoire"); categories.add("Action"); + String description = "Je suis un livre qui contient des phrases composees de mots qui sont eux memes composes de lettres faits a partir d'encre sur du papier relie par une couverture cartonnee"; + String language = "Francais"; + Book book = Book.builder() + .isbn(ISBN) + .title(title) + .author(author) + .publisher(publisher) + .date(date) + .price(price) + .initialStock(initialStock) + .categories(categories) + .description(description) + .language(language) + .build(); + + book.addStock(0); + + assertEquals(50, book.getInitialStock()); + } + + @Test + @DisplayName("removeStock should correctly decrement points") + void testRemoveStock() throws IllegalBookStockException { + String ISBN = "1234567890123"; + String title = "LivreRandom"; + String author = "John Doe"; + String publisher = "EditeurRandom"; + Date date = Date.from(Instant.now()); + double price = 5.99; + int initialStock = 50; + ArrayList categories = new ArrayList<>(); categories.add("Histoire"); categories.add("Action"); + String description = "Je suis un livre qui contient des phrases composees de mots qui sont eux memes composes de lettres faits a partir d'encre sur du papier relie par une couverture cartonnee"; + String language = "Francais"; + Book book = Book.builder() + .isbn(ISBN) + .title(title) + .author(author) + .publisher(publisher) + .date(date) + .price(price) + .initialStock(initialStock) + .categories(categories) + .description(description) + .language(language) + .build(); + int stockToRemove = 20; + int expectedStock = 30; + + book.removeStock(stockToRemove); + + assertEquals(expectedStock, book.getInitialStock()); + } + + @Test + @DisplayName("removeStock should throw exception when trying to remove more points than available") + void testRemoveTooManyStock() { + String ISBN = "1234567890123"; + String title = "LivreRandom"; + String author = "John Doe"; + String publisher = "EditeurRandom"; + Date date = Date.from(Instant.now()); + double price = 5.99; + int initialStock = 50; + ArrayList categories = new ArrayList<>(); categories.add("Histoire"); categories.add("Action"); + String description = "Je suis un livre qui contient des phrases composees de mots qui sont eux memes composes de lettres faits a partir d'encre sur du papier relie par une couverture cartonnee"; + String language = "Francais"; + Book book = Book.builder() + .isbn(ISBN) + .title(title) + .author(author) + .publisher(publisher) + .date(date) + .price(price) + .initialStock(initialStock) + .categories(categories) + .description(description) + .language(language) + .build(); + int pointsToRemove = 75; + + IllegalBookStockException exception = assertThrows( + IllegalBookStockException.class, + () -> book.removeStock(pointsToRemove) + ); + + assertEquals(50, book.getInitialStock()); + assertTrue(exception.getMessage().contains(String.valueOf(pointsToRemove))); + assertTrue(exception.getMessage().contains(String.valueOf(book.getInitialStock()))); + } + + @Test + @DisplayName("removeStock should handle removing zero points correctly") + void testRemoveZeroStock() throws IllegalBookStockException { + String ISBN = "1234567890123"; + String title = "LivreRandom"; + String author = "John Doe"; + String publisher = "EditeurRandom"; + Date date = Date.from(Instant.now()); + double price = 5.99; + int initialStock = 50; + ArrayList categories = new ArrayList<>(); categories.add("Histoire"); categories.add("Action"); + String description = "Je suis un livre qui contient des phrases composees de mots qui sont eux memes composes de lettres faits a partir d'encre sur du papier relie par une couverture cartonnee"; + String language = "Francais"; + Book book = Book.builder() + .isbn(ISBN) + .title(title) + .author(author) + .publisher(publisher) + .date(date) + .price(price) + .initialStock(initialStock) + .categories(categories) + .description(description) + .language(language) + .build(); + + book.removeStock(0); + + assertEquals(50, book.getInitialStock()); + } + + } + + + +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundExceptionTest.java new file mode 100644 index 0000000..c652b3b --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/BookNotFoundExceptionTest.java @@ -0,0 +1,60 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.exception; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.Random; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class BookNotFoundExceptionTest { + + private String randomISBN(){ + Random r = new Random(); + String ret = ""; + for(int i=0;i<13;i++){ + int e = r.nextInt()%10; + ret = ret + e; + } + return ret; + } + + @Test + @DisplayName("Exception message should contain the UUID provided") + void testExceptionMessageContainsISBN() { + String ISBN = randomISBN(); + + BookNotFoundException exception = new BookNotFoundException(ISBN); + + String expectedMessage = String.format("The book with ISBN %s does not exist", ISBN); + assertEquals(expectedMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should use the correct constant message format") + void testExceptionUsesConstantMessageFormat() { + String ISBN = randomISBN(); + + BookNotFoundException exception = new BookNotFoundException(ISBN); + + String expectedFormatWithPlaceholder = "The book with ISBN {0} does not exist"; + assertEquals(BookNotFoundException.THE_BOOK_WITH_ISBN_DOES_NOT_EXIST_MESSAGE, + expectedFormatWithPlaceholder); + assertTrue(exception.getMessage().contains(ISBN)); + } + + @Test + @DisplayName("Exception should be properly thrown and caught") + void testExceptionCanBeThrownAndCaught() { + String ISBN = randomISBN(); + + try { + throw new BookNotFoundException(ISBN); + } catch (BookNotFoundException e) { + String expectedMessage = String.format("The book with ISBN %s does not exist", ISBN); + assertEquals(expectedMessage, e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/IllegalBookStockExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/IllegalBookStockExceptionTest.java new file mode 100644 index 0000000..1d5a584 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/IllegalBookStockExceptionTest.java @@ -0,0 +1,71 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.exception; + +import java.text.MessageFormat; + +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.IllegalBookStockException; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class IllegalBookStockExceptionTest { + + @Test + @DisplayName("Exception message should contain the needed and actual stock") + void testExceptionMessageContainsStock() { + int neededStock = 100; + int actualStock = 50; + + IllegalBookStockException exception = new IllegalBookStockException(neededStock, actualStock); + + String expectedMessage = "Cannot remove 100 stock from 50 stock"; + assertEquals(expectedMessage, exception.getMessage()); + } + + @ParameterizedTest + @CsvSource({ + "100, 50", + "75, 25", + "200, 150", + "1000, 750" + }) + @DisplayName("Exception message should be formatted correctly for different stock values") + void testExceptionMessageForDifferentStockValues(int neededStock, int actualStock) { + IllegalBookStockException exception = new IllegalBookStockException(neededStock, actualStock); + + String expectedMessage = MessageFormat.format(IllegalBookStockException.CANNOT_REMOVE_STOCK, neededStock, actualStock); + assertEquals(expectedMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should use the correct constant message format") + void testExceptionUsesConstantMessageFormat() { + int neededStock = 100; + int actualStock = 50; + + IllegalBookStockException exception = new IllegalBookStockException(neededStock, actualStock); + + String expectedFormatWithPlaceholder = "Cannot remove {0} stock from {1} stock"; + assertEquals(IllegalBookStockException.CANNOT_REMOVE_STOCK, + expectedFormatWithPlaceholder); + assertTrue(exception.getMessage().contains(String.valueOf(neededStock))); + assertTrue(exception.getMessage().contains(String.valueOf(actualStock))); + } + + @Test + @DisplayName("Exception should be properly thrown and caught") + void testExceptionCanBeThrownAndCaught() { + int neededStock = 100; + int actualStock = 50; + + try { + throw new IllegalBookStockException(neededStock, actualStock); + } catch (IllegalBookStockException e) { + String expectedMessage = String.format("Cannot remove %d stock from %d stock", neededStock, actualStock); + assertEquals(expectedMessage, e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookExceptionTest.java new file mode 100644 index 0000000..cec5712 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/exception/NotValidBookExceptionTest.java @@ -0,0 +1,63 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.book.exception; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class NotValidBookExceptionTest { + + @Test + @DisplayName("Exception should be created with the provided message") + void testExceptionCreation() { + String errorMessage = "Customer data is not valid"; + + NotValidBookException exception = new NotValidBookException(errorMessage); + + assertEquals(errorMessage, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = { + "ISBN format is invalid", + "Title cannot be empty", + "Author cannot be empty", + "Publisher cannot be empty", + "Price needs to be positive", + "Stock cannot go below 0", + }) + @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 = "Required field is missing"; + + 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 = "Invalid book data"; + + try { + throw new NotValidBookException(errorMessage); + } catch (Exception e) { + assertEquals(NotValidBookException.class, e.getClass()); + assertEquals(errorMessage, e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepositoryTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepositoryTest.java new file mode 100644 index 0000000..972589d --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/repository/BookRepositoryTest.java @@ -0,0 +1,262 @@ +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.time.Instant; +import java.util.*; + +import static org.junit.jupiter.api.Assertions.*; + +class BookRepositoryTest { + + private BookRepository repository; + private Book book1; + private Book book2; + + @BeforeEach + void setUp() { + repository = new BookRepository(); + ArrayList categories = new ArrayList<>(); categories.add("Histoire"); categories.add("Action"); + + book1 = Book.builder() + .isbn("1234567890123") + .title("Livre 1") + .author("John Doe") + .publisher("Editeur") + .date(Date.from(Instant.now())) + .price(5.99) + .initialStock(50) + .categories(categories) + .description("Description") + .language("Francais") + .build(); + + book2 = Book.builder() + .isbn("3210987654321") + .title("Livre 2") + .author("Jane Smith") + .publisher("Editeur") + .date(Date.from(Instant.now())) + .price(8.54) + .initialStock(42) + .categories(categories) + .description("Description") + .language("Francais") + .build(); + } + + @Test + @DisplayName("New repository should be empty") + void testNewRepositoryIsEmpty() { + List books = repository.findAll(); + + assertTrue(books.isEmpty()); + assertEquals(0, books.size()); + } + + @Nested + @DisplayName("Save operations") + class SaveOperations { + + @Test + @DisplayName("Save should add a new book") + void testSaveNewBook() { + Book savedBook = repository.save(book1); + + assertEquals(1, repository.findAll().size()); + assertEquals(book1.getIsbn(), savedBook.getIsbn()); + assertEquals(book1.getTitle(), savedBook.getTitle()); + assertEquals(book1.getAuthor(), savedBook.getAuthor()); + assertEquals(book1.getPublisher(), savedBook.getPublisher()); + assertEquals(book1, savedBook.getDate()); + assertEquals(book1.getPrice(), savedBook.getPrice()); + assertEquals(book1.getInitialStock(), savedBook.getInitialStock()); + assertEquals(book1.getCategories(), savedBook.getCategories()); + assertEquals(book1.getDescription(), savedBook.getDescription()); + assertEquals(book1.getLanguage(), savedBook.getLanguage()); + } + + @Test + @DisplayName("Save should update existing book with same ISBN") + void testSaveUpdatesExistingBook() { + repository.save(book1); + + String isbn = book1.getIsbn(); + ArrayList categories = book1.getCategories(); + Book updatedBook = Book.builder() + .isbn(isbn) + .title("Livre 1") + .author("John Updated") + .publisher("Editeur") + .date(Date.from(Instant.now())) + .price(4.99) + .initialStock(42) + .categories(categories) + .description("Description") + .language("Francais") + .build(); + + Book savedBook = repository.save(updatedBook); + + assertEquals(1, repository.findAll().size()); + assertEquals(book1.getIsbn(), savedBook.getIsbn()); + assertEquals(book1.getTitle(), savedBook.getTitle()); + assertEquals(book1.getAuthor(), savedBook.getAuthor()); + assertEquals(book1.getPublisher(), savedBook.getPublisher()); + assertEquals(book1, savedBook.getDate()); + assertEquals(book1.getPrice(), savedBook.getPrice()); + assertEquals(book1.getInitialStock(), savedBook.getInitialStock()); + assertEquals(book1.getCategories(), savedBook.getCategories()); + assertEquals(book1.getDescription(), savedBook.getDescription()); + assertEquals(book1.getLanguage(), savedBook.getLanguage()); + } + + @Test + @DisplayName("Save multiple books should add all of them") + void testSaveMultipleBooks() { + repository.save(book1); + repository.save(book2); + + List books = repository.findAll(); + + assertEquals(2, books.size()); + assertTrue(books.contains(book1)); + assertTrue(books.contains(book2)); + } + } + + @Nested + @DisplayName("Find operations") + class FindOperations { + + @BeforeEach + void setUpBooks() { + repository.save(book1); + repository.save(book2); + } + + @Test + @DisplayName("FindAll should return all books") + void testFindAll() { + List books = repository.findAll(); + + assertEquals(2, books.size()); + assertTrue(books.contains(book1)); + assertTrue(books.contains(book2)); + } + + @Test + @DisplayName("FindById should return book with matching ID") + void testFindById() { + Optional foundBook = repository.findByISBN(book1.getIsbn()); + + assertTrue(foundBook.isPresent()); + assertEquals(book1.getIsbn(), foundBook.get().getIsbn()); + assertEquals(book1.getTitle(), foundBook.get().getTitle()); + assertEquals(book1.getAuthor(), foundBook.get().getAuthor()); + assertEquals(book1.getPublisher(), foundBook.get().getPublisher()); + assertEquals(book1, foundBook.get().getDate()); + assertEquals(book1.getPrice(), foundBook.get().getPrice()); + assertEquals(book1.getInitialStock(), foundBook.get().getInitialStock()); + assertEquals(book1.getCategories(), foundBook.get().getCategories()); + assertEquals(book1.getDescription(), foundBook.get().getDescription()); + assertEquals(book1.getLanguage(), foundBook.get().getLanguage()); + } + + @Test + @DisplayName("FindById should return empty Optional when ID doesn't exist") + void testFindByIdNotFound() { + String nonExistentISBN = "1115553331112"; + + Optional foundBook = repository.findByISBN(nonExistentISBN); + + assertTrue(foundBook.isEmpty()); + } + + @Test + @DisplayName("FindByISBN should return book with matching ISBN") + void testFindByISBN() { + Optional foundBook = repository.findByISBN("1234567890123"); + + assertTrue(foundBook.isPresent()); + assertEquals(book1.getIsbn(), foundBook.get().getIsbn()); + assertEquals(book1.getTitle(), foundBook.get().getTitle()); + assertEquals(book1.getAuthor(), foundBook.get().getAuthor()); + assertEquals(book1.getPublisher(), foundBook.get().getPublisher()); + assertEquals(book1, foundBook.get().getDate()); + assertEquals(book1.getPrice(), foundBook.get().getPrice()); + assertEquals(book1.getInitialStock(), foundBook.get().getInitialStock()); + assertEquals(book1.getCategories(), foundBook.get().getCategories()); + assertEquals(book1.getDescription(), foundBook.get().getDescription()); + assertEquals(book1.getLanguage(), foundBook.get().getLanguage()); + } + + @Test + @DisplayName("FindByISBN should return empty Optional when ISBN doesn't exist") + void testFindByISBNNotFound() { + Optional foundBook = repository.findByISBN("0000000000000"); + + assertTrue(foundBook.isEmpty()); + } + } + + @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); + + List books = repository.findAll(); + + assertEquals(1, books.size()); + assertFalse(books.contains(book1)); + assertTrue(books.contains(book2)); + } + + @Test + @DisplayName("DeleteAll should remove all books") + void testDeleteAll() { + repository.deleteAll(); + + List books = repository.findAll(); + + assertTrue(books.isEmpty()); + assertEquals(0, books.size()); + } + + @Test + @DisplayName("Delete should not throw exception when book doesn't exist") + void testDeleteNonExistentBook() { + ArrayList categories = new ArrayList<>(); categories.add("Histoire"); categories.add("Action"); + Book nonExistentBook = Book.builder() + .isbn("0000000000000") + .title("Livre Non existant") + .author("John Doe") + .publisher("Editeur") + .date(Date.from(Instant.now())) + .price(5.99) + .initialStock(50) + .categories(categories) + .description("Description") + .language("Francais") + .build(); + + assertDoesNotThrow(() -> repository.delete(nonExistentBook)); + + assertEquals(2, repository.findAll().size()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCaseTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCaseTest.java index 25b584a..d057f4f 100644 --- a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCaseTest.java +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/usecase/BookUseCaseTest.java @@ -1,5 +1,11 @@ package fr.iut_fbleau.but3.dev62.mylibrary.book.usecase; +import fr.iut_fbleau.but3.dev62.mylibrary.book.BookDTO; +import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.BookNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException; +import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -9,6 +15,9 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; @@ -32,21 +41,22 @@ public class BookUseCaseTest { @BeforeEach void setUp() { - + ArrayList categories = new ArrayList<>(); categories.add("Histoire"); categories.add("Action"); + Date date = Date.from(Instant.now()); testBook = Book.builder() .isbn(bookISBN) .title("LivreRandom") .author("John Doe") .publisher("RandomPublisher") - .publicationDate(date) + .date(date) .price(12.5) .initialStock(50) - .categories(cat) + .categories(categories) .description("Je suis un livre qui est composé de mots.") .language("Francais") .build(); - validBookInfo = new BookInfo("LivreRandom", "John Doe", "RandomPublisher", date, 12.5, 50, cat, "Je suis un livre qui est composé de mots.", "Francais"); + validBookInfo = new BookInfo("bookISBN","LivreRandom", "John Doe", "RandomPublisher", date, 12.5, 50, categories, "Je suis un livre qui est composé de mots.", "Francais"); } @@ -58,9 +68,9 @@ public class BookUseCaseTest { @Test @DisplayName("Should register book when valid data is provided") void testRegisterBookWithValidData() throws NotValidBookException { - when(BookRepository.save(any(Book.class))).thenReturn(testBook); + when(bookRepository.save(any(Book.class))).thenReturn(testBook); - String registeredISBN = BookUseCase.registerBook(validBookInfo); + String registeredISBN = bookUseCase.registerBook(validBookInfo); assertNotNull(registeredISBN); assertEquals(bookISBN, registeredISBN); @@ -70,7 +80,7 @@ public class BookUseCaseTest { @Test @DisplayName("Should throw exception when book data is not valid") void testRegisterBookWithInvalidData() { - BookInfo invalidBookInfo = new BookInfo("", "", ""); + BookInfo invalidBookInfo = new BookInfo("", "", "", "", Date.from(Instant.now()),-78, -1, null, "", ""); assertThrows(NotValidBookException.class, () -> bookUseCase.registerBook(invalidBookInfo)); @@ -91,14 +101,14 @@ public class BookUseCaseTest { @Test @DisplayName("Should return book when ISBN exists") void testFindBookByISBN() { - when(BookRepository.findBookByISBN("1234567890123")).thenReturn(Optional.of(testISBN)); + when(bookRepository.findByISBN("1234567890123")).thenReturn(Optional.empty()); Optional foundBook = bookUseCase.findBookByISBN("1234567890123"); assertTrue(foundBook.isPresent()); - assertEquals(testBook.getISBN(), foundBook.get().getISBN()); - assertEquals(testBook.getName(), foundBook.get().getName()); - verify(bookRepository, times(1)).findBookByISBN("1234567890123"); + assertEquals(testBook.getIsbn(), foundBook.get().getIsbn()); + assertEquals(testBook.getTitle(), foundBook.get().getTitle()); + verify(bookRepository, times(1)).findByISBN("1234567890123"); } @Test @@ -124,16 +134,16 @@ public class BookUseCaseTest { when(bookRepository.findByISBN(bookISBN)).thenReturn(Optional.of(testBook)); doNothing().when(bookRepository).delete(testBook); - bookUseCase.deleteBook(bookId); + bookUseCase.deleteBook(bookISBN); - verify(bookRepository, times(1)).findByISBN(bookId); + verify(bookRepository, times(1)).findByISBN(bookISBN); verify(bookRepository, times(1)).delete(testBook); } @Test @DisplayName("Should throw exception when book ISBN doesn't exist") void testDeleteBookNotFound() { - ISBN nonExistentId = ISBN.randomISBN(); + String nonExistentISBN = "0000000000001"; when(bookRepository.findByISBN(nonExistentISBN)).thenReturn(Optional.empty()); assertThrows(BookNotFoundException.class, diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidatorTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidatorTest.java new file mode 100644 index 0000000..5eb0007 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/book/validator/BookValidatorTest.java @@ -0,0 +1,244 @@ +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 fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.NotValidCustomerException; +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.Instant; +import java.util.ArrayList; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.*; + +class BookValidatorTest { + + @Test + @DisplayName("Should validate book with valid data") + void testValidateValidBook() { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo validBook = new BookInfo("1234567891234", "Livre", "john doe","editeur", Date.from(Instant.now()),5.99,15,categories,"description","langue"); + + assertDoesNotThrow(() -> BookValidator.validate(validBook)); + } + + @Nested + @DisplayName("ISBN validation tests") + class ISBNValidationTests { + + @Test + @DisplayName("Should throw exception when ISBN is blank") + void testValidateBlankISBN() { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithBlankISBN = new BookInfo("", "Livre", "john doe","editeur", Date.from(Instant.now()),5.99,15,categories,"description","langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithBlankISBN) + ); + + assertEquals(BookValidator.ISBN_CANNOT_BE_BLANK, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {" ", " ", "\t", "\n"}) + @DisplayName("Should throw exception when ISBN contains only whitespace") + void testValidateWhitespaceISBN(String whitespace) { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithWhitespaceISBN = new BookInfo(whitespace, "Livre", "john doe","editeur", Date.from(Instant.now()),5.99,15,categories,"description","langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithWhitespaceISBN) + ); + + assertEquals(BookValidator.ISBN_CANNOT_BE_BLANK, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {"hfoahdiehfyui", "123456789 0123", "123", "12345678901234","123456789 123"}) + @DisplayName("Should throw exception when ISBN is not a valid format") + void testValidateNotValidISBN(String testedISBN) { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithInvalidISBN = new BookInfo(testedISBN, "Livre", "john doe","editeur", Date.from(Instant.now()),5.99,15,categories,"description","langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithInvalidISBN) + ); + + assertEquals(BookValidator.ISBN_IS_NOT_VALID, exception.getMessage()); + } + + } + + + @Nested + @DisplayName("Title validation tests") + class TitleValidationTests { + + @Test + @DisplayName("Should throw exception when Title is blank") + void testValidateBlankTitle() { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithBlankTitle = new BookInfo("1234567890123", "", "john doe","editeur", Date.from(Instant.now()),5.99,15,categories,"description","langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithBlankTitle) + ); + + 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) { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithBlankTitle = new BookInfo("1234567890123", whitespace, "john doe","editeur", Date.from(Instant.now()),5.99,15,categories,"description","langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithBlankTitle) + ); + + 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() { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithBlankAuthor = new BookInfo("1234567890123", "Livre", "","editeur", Date.from(Instant.now()),5.99,15,categories,"description","langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithBlankAuthor) + ); + + assertEquals(BookValidator.AUTHOR_CANNOT_BE_BLANK, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {" ", " ", "\t", "\n"}) + @DisplayName("Should throw exception when Author contains only whitespace") + void testValidateWhitespaceAuthor(String whitespace) { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithBlankAuthor = new BookInfo("1234567890123", "Livre", whitespace,"editeur", Date.from(Instant.now()),5.99,15,categories,"description","langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithBlankAuthor) + ); + + 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() { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithBlankPublisher = new BookInfo("1234567890123", "Livre", "Auteur","", Date.from(Instant.now()),5.99,15,categories,"description","langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithBlankPublisher) + ); + + assertEquals(BookValidator.PUBLISHER_CANNOT_BE_BLANK, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {" ", " ", "\t", "\n"}) + @DisplayName("Should throw exception when Publisher contains only whitespace") + void testValidateWhitespacePublisher(String whitespace) { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithBlankPublisher = new BookInfo("1234567890123", "Livre", "Auteur",whitespace, Date.from(Instant.now()),5.99,15,categories,"description","langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithBlankPublisher) + ); + + 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() { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithNegativePrice = new BookInfo("1234567890123", "Livre", "Auteur","Editeur", Date.from(Instant.now()),-4.98,15,categories,"description","langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithNegativePrice) + ); + + assertEquals(BookValidator.PRICE_CANNOT_BE_NEGATIVE, exception.getMessage()); + } + + @Test + @DisplayName("Should throw exception when Price is equal to zero") + void testValidatePriceEqualToZero() { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithPriceEqualToZero = new BookInfo("1234567890123", "Livre", "Auteur","Editeur", Date.from(Instant.now()),0,15,categories,"description","langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithPriceEqualToZero) + ); + + assertEquals(BookValidator.PRICE_CANNOT_BE_EQUAL_TO_ZERO, exception.getMessage()); + } + + + } + + @Nested + @DisplayName("Initial Stock validation tests") + class InitialStockValidationTests { + + @Test + @DisplayName("Should throw exception when Initial Stock is negative") + void testValidateNegativePrice() { + ArrayList categories = new ArrayList<>(); categories.add("Categorie1"); categories.add("Categorie2"); + BookInfo bookWithNegativeInitialStock = new BookInfo("1234567890123", "Livre", "Auteur", "Editeur", Date.from(Instant.now()), 5.12, -15, categories, "description", "langue"); + + NotValidBookException exception = assertThrows( + NotValidBookException.class, + () -> BookValidator.validate(bookWithNegativeInitialStock) + ); + + assertEquals(BookValidator.INITIAL_STOCK_CANNOT_BE_NEGATIVE, exception.getMessage()); + } + + } + + + + +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/client/CustomerSteps.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/client/CustomerSteps.java index e71f786..bd2226e 100644 --- a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/client/CustomerSteps.java +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/client/CustomerSteps.java @@ -220,4 +220,5 @@ public class CustomerSteps { assertEquals(errorMessage, illegalCustomerPointException.getMessage()); } + } diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/livre/BookSteps.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/livre/BookSteps.java new file mode 100644 index 0000000..8a1baf1 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/livre/BookSteps.java @@ -0,0 +1,222 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.features.livre; + + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +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 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 fr.iut_fbleau.but3.dev62.mylibrary.book.exception.BookNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.BookNotFoundExceptionTest; +import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException; +import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository; +import fr.iut_fbleau.but3.dev62.mylibrary.book.usecase.BookUseCase; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerDTO; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.NotValidCustomerException; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.usecase.CustomerUseCase; +import io.cucumber.datatable.DataTable; +import io.cucumber.java.ParameterType; +import io.cucumber.java.en.And; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.cucumber.java.en.When; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.Instant; +import java.util.*; + + +public class BookSteps { + + private final Map bookISBN = new HashMap<>(); + private final BookRepository bookRepository = new BookRepository(); + private final BookUseCase bookUseCase = new BookUseCase(bookRepository); + private String bookRegistration; + private Book updatedBook; + private Optional bookByISBN; + private NotValidBookException notValidBookException; + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-mm-dd", Locale.ENGLISH); + + private ArrayList listOfStrings(String arg) { + return new ArrayList(Arrays.asList(arg.split(",\\s"))); + } + + + + @Given("le systeme possedent les livres suivants :") + public void theSystemHasTheFollowingBooks(DataTable dataTable) throws ParseException { + int size = bookRepository.findAll().size(); + + if (size > 0) { + bookRepository.deleteAll(); + } + + List> books = dataTable.asMaps(String.class, String.class); + + for (Map book : books) { + String ISBN = book.get("ISBN"); + Book newBook = Book.builder() + .isbn(ISBN) + .title(book.get("titre")) + .author(book.get("auteur")) + .publisher(book.get("editeur")) + .date(formatter.parse(book.get("datePublication"))) + .price(Double.parseDouble(book.get("prix"))) + .initialStock(Integer.parseInt(book.get("stockInitial"))) + .categories(listOfStrings(book.get("categories"))) + .description(book.get("description")) + .language(book.get("langue")) + .build(); + Book save = bookRepository.save(newBook); + bookISBN.put(ISBN, save.getIsbn()); + } + + assertEquals(books.size(), bookRepository.findAll().size()); + } + + @When("Je cree un nouveau livre avec les informations suivantes :") + public void iCreateANewBookWithTheFollowingInformation(DataTable dataTable) throws NotValidBookException, ParseException { + List> rows = dataTable.asMaps(String.class, String.class); + + Map bookInfo = rows.getFirst(); + + + BookInfo newBook = new BookInfo( + bookInfo.get("ISBN"), + bookInfo.get("titre"), + bookInfo.get("auteur"), + bookInfo.get("editeur"), + formatter.parse(bookInfo.get("datePublication")), + Double.parseDouble(bookInfo.get("prix")), + Integer.parseInt(bookInfo.get("stockInitial")), + listOfStrings(bookInfo.get("categories")), + bookInfo.get("description"), + bookInfo.get("langue") + ); + + bookRegistration = bookUseCase.registerBook(newBook); + } + + + + @Then("Un nouveau livre est cree") + public void aNewBookIsCreated(){ + assertNotNull(bookRegistration); + } + + @When("Je modifie le livre avec l'ISBN {string} avec les informations suivantes:") + public void iUpdateABookWithTheFollowingInformation(String isbn, DataTable dataTable) throws BookNotFoundException, NotValidBookException, ParseException { + List> rows = dataTable.asMaps(String.class, String.class); + + Map bookData = rows.getFirst(); + BookInfo bookInfo = new BookInfo( + bookData.get("ISBN"), + bookData.get("titre"), + bookData.get("auteur"), + bookData.get("editeur"), + formatter.parse(bookData.get("datePublication")), + Double.parseDouble(bookData.get("prix")), + Integer.parseInt(bookData.get("stockInitial")), + listOfStrings(bookData.get("categories")), + bookData.get("description"), + bookData.get("langue") + ); + bookUseCase.updateBook(isbn, bookInfo); + + } + + @Then("Le livre {string} a les informations suivantes:") + public void theBookHasFollowingInformations(String isbn, DataTable dataTable) throws ParseException { + List> rows = dataTable.asMaps(String.class, String.class); + + Map updatedData = rows.getFirst(); + + + updatedBook = bookRepository.findByISBN(isbn).orElseThrow(); + assertEquals(updatedData.get("ISBN"), updatedBook.getIsbn()); + assertEquals(updatedData.get("titre"), updatedBook.getTitle()); + assertEquals(updatedData.get("auteur"), updatedBook.getAuthor()); + assertEquals(updatedData.get("editeur"), updatedBook.getPublisher()); + assertEquals(formatter.parse(updatedData.get("datePublication")), updatedBook.getDate()); + assertEquals(Double.parseDouble(updatedData.get("prix")), updatedBook.getPrice()); + assertEquals(Integer.parseInt(updatedData.get("stockInitial")), updatedBook.getInitialStock()); + assertEquals(listOfStrings(updatedData.get("categories")), updatedBook.getCategories()); + assertEquals(updatedData.get("description"), updatedBook.getDescription()); + assertEquals(updatedData.get("langue"), updatedBook.getLanguage()); + } + + @When("Je supprime le livre avec l'ISBN {string}") + public void iDeleteTheFollowingBook(String isbn) throws BookNotFoundException { + bookUseCase.deleteBook(isbn); + } + + @Then("Le livre {string} n'existe plus dans le systeme") + public void theBookDoesNotExistInTheSystem(String isbn){ + assertFalse(bookRepository.existsByISBN(isbn)); + } + + @And("Le systeme contient {int} livre") + public void theSystemContainsThisMuchBooks(int nBooks){ + assertEquals(nBooks,bookRepository.findAll().size()); + } + + @When("Je demande les informations du lirve avec l'ISBN {string}") + public void iAskForTheBookWithISBN(String isbn){ + bookByISBN = bookUseCase.findBookByISBN(isbn); + } + + @Then("Je recupere les informations suivantes:") + public void iGetTheFollowingInformation(DataTable dataTable) throws ParseException { + List> books = dataTable.asMaps(String.class, String.class); + Map bookInfo = books.getFirst(); + assertTrue(bookByISBN.isPresent()); + BookDTO bookDTO = bookByISBN.get(); + assertEquals(bookInfo.get("ISBN"), bookDTO.getIsbn()); + assertEquals(bookInfo.get("titre"), bookDTO.getTitle()); + assertEquals(bookInfo.get("auteur"), bookDTO.getAuthor()); + assertEquals(bookInfo.get("editeur"), bookDTO.getPublisher()); + assertEquals(formatter.parse(bookInfo.get("datePublication")), bookDTO.getDate()); + assertEquals(Double.parseDouble(bookInfo.get("prix")), bookDTO.getPrice()); + assertEquals(Integer.parseInt(bookInfo.get("stockInitial")), bookDTO.getInitialStock()); + assertEquals(listOfStrings(bookInfo.get("categories")), bookDTO.getCategories()); + assertEquals(bookInfo.get("description"), bookDTO.getDescription()); + assertEquals(bookInfo.get("langue"), bookDTO.getLanguage()); + } + + @When("J'essaie de creer un nouveau livre avec les informations suivantes :") + public void iTryToCreateANewBookWithFollowingInformation(DataTable dataTable) throws ParseException { + List> rows = dataTable.asMaps(String.class, String.class); + + Map bookInfo = rows.getFirst(); + + BookInfo newBook = new BookInfo( + bookInfo.get("ISBN"), + bookInfo.get("titre"), + bookInfo.get("auteur"), + bookInfo.get("editeur"), + formatter.parse(bookInfo.get("datePublication")), + Double.parseDouble(bookInfo.get("prix")), + Integer.parseInt(bookInfo.get("stockInitial")), + listOfStrings(bookInfo.get("categories")), + bookInfo.get("description"), + bookInfo.get("langue") + ); + + notValidBookException = assertThrows(NotValidBookException.class, () -> bookUseCase.registerBook(newBook)); + } + + @Then("La creation echoue") + public void creationFailed(){ + assertNotNull(notValidBookException); + } + + +} diff --git a/src/test/resources/features/livre.feature b/src/test/resources/features/livre.feature new file mode 100644 index 0000000..9829a7d --- /dev/null +++ b/src/test/resources/features/livre.feature @@ -0,0 +1,42 @@ +# language: en + +Feature: Gerez les livres + Background: + Given le systeme possedent les livres suivants : + | ISBN | titre | auteur | editeur | datePublication | prix | stockInitial | categories | description | langue | + | 9781234567890 | Les Mysteres de l'IA | Jean Dupont | TechEditions | 2024-01-15 | 29.99 | 10 | [Technologie, Science] | Un livre fascinant sur l'IA. | Francais | + | 9789876543210 | L'Art de la Guerre Moderne | Claire Martin | StrategeBooks | 2023-06-10 | 35.50 | 5 | [Strategie, Histoire] | Analyse des conflits modernes et tactiques. | Francais | + | 9781112223334 | Cuisine du Monde | Pierre Lemoine | GourmetEditions | 2022-09-20 | 24.90 | 20 | [Cuisine, Voyage] | Un tour du monde gastronomique en recettes. | Francais | + | 9785556667778 | Python pour Debutants | Alice Bernard | CodeMaster | 2021-04-05 | 19.99 | 15 | [Programmation, Informatique] | Apprendre Python pas a pas avec des exercices. | Francais | + | 9791032719640 | My Hero Academia |Kohei Horikoshi | Ki-oon | 2025-02-06 | 6.95 | 900 | [Fantastique, Science-fiction, super-heros] | Retrouvez les super-heros du manga phenomene My Hero Academia ! | Francais | + + Scenario: Creer un nouveau livre + When Je cree un nouveau livre avec les informations suivantes : + | ISBN | titre | auteur | editeur | datePublication | prix | stockInitial | categories | description | langue | + | 9789998887776 | L'eveil Spirituel | Olivier Fontaine | ZenBooks | 2020-11-30 | 22.00 | 8 | [Developpement personnel, Spiritualite] | Un guide vers la pleine conscience et la paix interieure. | Francais | + Then Un nouveau livre est cree + + Scenario: Modifier les informations d'un livre + When Je modifie le livre avec l'ISBN "9791032719640" avec les informations suivantes: + | ISBN | titre | auteur | editeur | datePublication | prix | stockInitial | categories | description | langue | + | 9791032719640 | My Hero Academia | Kohei Horikoshi | Ki-oon | 2025-02-06 | 6.95 | 999 | [Fantastique, Science-fiction, super-heros] | Retrouvez les super-heros du manga phenomene My Hero Academia ! | Francais | + Then Le livre "9791032719640" a les informations suivantes: + | ISBN | titre | auteur | editeur | datePublication | prix | stockInitial | categories | description | langue | + | 9791032719640 | My Hero Academia | Kohei Horikoshi | Ki-oon | 2025-02-06 | 6.95 | 999 | [Fantastique, Science-fiction, super-heros] | Retrouvez les super-heros du manga phenomene My Hero Academia ! | Francais | + + Scenario: Supprimer un livre + When Je supprime le livre avec l'ISBN "9791032719640" + Then Le livre "9791032719640" n'existe plus dans le systeme + And Le systeme contient 4 livre + + Scenario: Recuperer les informations d'un livre + When Je demande les informations du lirve avec l'ISBN "9789876543210" + Then Je recupere les informations suivantes: + | ISBN | titre | auteur | editeur | datePublication | prix | stockInitial | categories | description | langue | + | 9789876543210 | L'Art de la Guerre Moderne | Claire Martin | StrategeBooks | 2023-06-10 | 35.50 | 5 | [Strategie, Histoire] | Analyse des conflits modernes et tactiques. | Francais | + + Scenario: Tentative de creation d'un livre incorrect + When J'essaie de creer un nouveau livre avec les informations suivantes : + | ISBN | titre | auteur | editeur | datePublication | prix | stockInitial | categories | description | langue | + | abcefg | L'Art de la Guerre Moderne | Claire Martin | StrategeBooks | 2023-06-10 | 35.50 | 5 | [Strategie, Histoire] | Analyse des conflits modernes et tactiques. | Francais | + Then La creation echoue