Merge remote-tracking branch 'origin/main'

This commit is contained in:
2025-06-11 15:41:24 +02:00
22 changed files with 1716 additions and 14 deletions

View File

@@ -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<String> categories;
private final String description;
private final String language;
}

View File

@@ -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,
ArrayList<String>categories,
String description,
String language){
}

View File

@@ -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();
}
}

View File

@@ -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<String> 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;
}
}

View File

@@ -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));
}
}

View File

@@ -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));
}
}

View File

@@ -0,0 +1,8 @@
package fr.iut_fbleau.but3.dev62.mylibrary.book.exception;
public class NotValidBookException extends Exception {
public NotValidBookException(String message) {
super(message);
}
}

View File

@@ -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<Book> books = new ArrayList<>();
public List<Book> findAll() {
return books;
}
public void deleteAll() {
books.clear();
}
public Book save(Book newBook) {
Optional<Book> optionalBookWithSameId = this.findByISBN(newBook.getIsbn());
optionalBookWithSameId.ifPresent(books::remove);
this.books.add(newBook);
return newBook;
}
public Optional<Book> 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);
}
}

View File

@@ -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<BookDTO> findBookByISBN(String isbn) {
Optional<Book> 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<Book> optionalBookById = bookRepository.findByISBN(isbn);
if (optionalBookById.isEmpty()) {
throw new BookNotFoundException(isbn);
}
return optionalBookById.get();
}
}

View File

@@ -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);
}
}
}

View File

@@ -8,6 +8,7 @@ import lombok.Getter;
@Builder @Builder
@Getter @Getter
public class Customer { public class Customer {
private UUID id; private UUID id;
private String firstName; private String firstName;

View File

@@ -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<String> 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<String> 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());
}
}

View File

@@ -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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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());
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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<String> 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<Book> 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<String> 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<Book> 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<Book> 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<Book> 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<Book> foundBook = repository.findByISBN(nonExistentISBN);
assertTrue(foundBook.isEmpty());
}
@Test
@DisplayName("FindByISBN should return book with matching ISBN")
void testFindByISBN() {
Optional<Book> 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<Book> 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<Book> 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<Book> 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<String> 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());
}
}
}

View File

@@ -1,5 +1,11 @@
package fr.iut_fbleau.but3.dev62.mylibrary.book.usecase; 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.BeforeEach;
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
@@ -9,6 +15,9 @@ import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.Optional; import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
@@ -32,21 +41,22 @@ public class BookUseCaseTest {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
ArrayList<String> categories = new ArrayList<>(); categories.add("Histoire"); categories.add("Action");
Date date = Date.from(Instant.now());
testBook = Book.builder() testBook = Book.builder()
.isbn(bookISBN) .isbn(bookISBN)
.title("LivreRandom") .title("LivreRandom")
.author("John Doe") .author("John Doe")
.publisher("RandomPublisher") .publisher("RandomPublisher")
.publicationDate(date) .date(date)
.price(12.5) .price(12.5)
.initialStock(50) .initialStock(50)
.categories(cat) .categories(categories)
.description("Je suis un livre qui est composé de mots.") .description("Je suis un livre qui est composé de mots.")
.language("Francais") .language("Francais")
.build(); .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 @Test
@DisplayName("Should register book when valid data is provided") @DisplayName("Should register book when valid data is provided")
void testRegisterBookWithValidData() throws NotValidBookException { 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); assertNotNull(registeredISBN);
assertEquals(bookISBN, registeredISBN); assertEquals(bookISBN, registeredISBN);
@@ -70,7 +80,7 @@ public class BookUseCaseTest {
@Test @Test
@DisplayName("Should throw exception when book data is not valid") @DisplayName("Should throw exception when book data is not valid")
void testRegisterBookWithInvalidData() { void testRegisterBookWithInvalidData() {
BookInfo invalidBookInfo = new BookInfo("", "", ""); BookInfo invalidBookInfo = new BookInfo("", "", "", "", Date.from(Instant.now()),-78, -1, null, "", "");
assertThrows(NotValidBookException.class, assertThrows(NotValidBookException.class,
() -> bookUseCase.registerBook(invalidBookInfo)); () -> bookUseCase.registerBook(invalidBookInfo));
@@ -91,14 +101,14 @@ public class BookUseCaseTest {
@Test @Test
@DisplayName("Should return book when ISBN exists") @DisplayName("Should return book when ISBN exists")
void testFindBookByISBN() { void testFindBookByISBN() {
when(BookRepository.findBookByISBN("1234567890123")).thenReturn(Optional.of(testISBN)); when(bookRepository.findByISBN("1234567890123")).thenReturn(Optional.empty());
Optional<BookDTO> foundBook = bookUseCase.findBookByISBN("1234567890123"); Optional<BookDTO> foundBook = bookUseCase.findBookByISBN("1234567890123");
assertTrue(foundBook.isPresent()); assertTrue(foundBook.isPresent());
assertEquals(testBook.getISBN(), foundBook.get().getISBN()); assertEquals(testBook.getIsbn(), foundBook.get().getIsbn());
assertEquals(testBook.getName(), foundBook.get().getName()); assertEquals(testBook.getTitle(), foundBook.get().getTitle());
verify(bookRepository, times(1)).findBookByISBN("1234567890123"); verify(bookRepository, times(1)).findByISBN("1234567890123");
} }
@Test @Test
@@ -124,16 +134,16 @@ public class BookUseCaseTest {
when(bookRepository.findByISBN(bookISBN)).thenReturn(Optional.of(testBook)); when(bookRepository.findByISBN(bookISBN)).thenReturn(Optional.of(testBook));
doNothing().when(bookRepository).delete(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); verify(bookRepository, times(1)).delete(testBook);
} }
@Test @Test
@DisplayName("Should throw exception when book ISBN doesn't exist") @DisplayName("Should throw exception when book ISBN doesn't exist")
void testDeleteBookNotFound() { void testDeleteBookNotFound() {
ISBN nonExistentId = ISBN.randomISBN(); String nonExistentISBN = "0000000000001";
when(bookRepository.findByISBN(nonExistentISBN)).thenReturn(Optional.empty()); when(bookRepository.findByISBN(nonExistentISBN)).thenReturn(Optional.empty());
assertThrows(BookNotFoundException.class, assertThrows(BookNotFoundException.class,

View File

@@ -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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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<String> 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());
}
}
}

View File

@@ -220,4 +220,5 @@ public class CustomerSteps {
assertEquals(errorMessage, illegalCustomerPointException.getMessage()); assertEquals(errorMessage, illegalCustomerPointException.getMessage());
} }
} }

View File

@@ -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<String, String> bookISBN = new HashMap<>();
private final BookRepository bookRepository = new BookRepository();
private final BookUseCase bookUseCase = new BookUseCase(bookRepository);
private String bookRegistration;
private Book updatedBook;
private Optional<BookDTO> bookByISBN;
private NotValidBookException notValidBookException;
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-mm-dd", Locale.ENGLISH);
private ArrayList<String> listOfStrings(String arg) {
return new ArrayList<String>(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<Map<String, String>> books = dataTable.asMaps(String.class, String.class);
for (Map<String, String> 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<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
Map<String, String> 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<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
Map<String, String> 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<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
Map<String, String> 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<Map<String, String>> books = dataTable.asMaps(String.class, String.class);
Map<String, String> 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<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
Map<String, String> 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);
}
}

View File

@@ -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