|
|
|
@@ -0,0 +1,345 @@
|
|
|
|
|
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.BookDetails;
|
|
|
|
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo;
|
|
|
|
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.BookSalesInfo;
|
|
|
|
|
import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
|
|
|
|
|
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;
|
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
|
|
|
import org.junit.jupiter.api.extension.ExtendWith;
|
|
|
|
|
import org.mockito.InjectMocks;
|
|
|
|
|
import org.mockito.Mock;
|
|
|
|
|
import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
|
|
|
|
|
|
|
import java.time.LocalDate;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Optional;
|
|
|
|
|
|
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
|
|
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 static org.mockito.ArgumentMatchers.any;
|
|
|
|
|
import static org.mockito.Mockito.*;
|
|
|
|
|
import static org.mockito.Mockito.never;
|
|
|
|
|
|
|
|
|
|
@ExtendWith(MockitoExtension.class)
|
|
|
|
|
public class BookUseCaseTest {
|
|
|
|
|
|
|
|
|
|
@Mock
|
|
|
|
|
private BookRepository bookRepository;
|
|
|
|
|
|
|
|
|
|
@InjectMocks
|
|
|
|
|
private BookUseCase bookUseCase;
|
|
|
|
|
|
|
|
|
|
private String bookIsbn;
|
|
|
|
|
private ArrayList<String> categories;
|
|
|
|
|
private LocalDate date;
|
|
|
|
|
private Book testBook;
|
|
|
|
|
private BookInfo validBookInfo;
|
|
|
|
|
private BookDetails validBookDetails;
|
|
|
|
|
private BookSalesInfo validBookSalesInfo;
|
|
|
|
|
|
|
|
|
|
@BeforeEach
|
|
|
|
|
void setUp() {
|
|
|
|
|
bookIsbn = "1234567891012";
|
|
|
|
|
date = LocalDate.of(2026, 3, 24);
|
|
|
|
|
categories = new ArrayList<>();
|
|
|
|
|
categories.add("Thriller");
|
|
|
|
|
categories.add("Biographie");
|
|
|
|
|
|
|
|
|
|
testBook = Book.builder()
|
|
|
|
|
.isbn(bookIsbn)
|
|
|
|
|
.title("La vie de Maxime")
|
|
|
|
|
.author("Marvin AUbert")
|
|
|
|
|
.editor("Kioon")
|
|
|
|
|
.date(date)
|
|
|
|
|
.price(12.99)
|
|
|
|
|
.stock(50)
|
|
|
|
|
.categories(categories)
|
|
|
|
|
.description("C'était un brave partit trop tôt")
|
|
|
|
|
.language("Français")
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
validBookInfo = new BookInfo(bookIsbn,"La vie de Maxime", "Marvin AUbert", "Kioon", date);
|
|
|
|
|
validBookSalesInfo = BookSalesInfo.builder()
|
|
|
|
|
.price(12.99)
|
|
|
|
|
.stock(50)
|
|
|
|
|
.build();
|
|
|
|
|
validBookDetails = BookDetails.builder()
|
|
|
|
|
.categories(categories)
|
|
|
|
|
.description("C'était un brave partit trop tôt")
|
|
|
|
|
.language("Français")
|
|
|
|
|
.build();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Nested
|
|
|
|
|
@DisplayName("Register Book tests")
|
|
|
|
|
class RegisterBookTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should register book when valid data is provided")
|
|
|
|
|
void testRegisterBookWithValidData() throws NotValidBookException {
|
|
|
|
|
when(bookRepository.save(any(Book.class))).thenReturn(testBook);
|
|
|
|
|
|
|
|
|
|
String registeredIsbn = bookUseCase.registerBook(validBookInfo);
|
|
|
|
|
|
|
|
|
|
assertNotNull(registeredIsbn);
|
|
|
|
|
assertEquals(bookIsbn, registeredIsbn);
|
|
|
|
|
verify(bookRepository, times(1)).save(any(Book.class));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should throw exception when book data is not valid")
|
|
|
|
|
void testRegisterBookWithInvalidData() {
|
|
|
|
|
BookInfo invalidBookInfo = new BookInfo(bookIsbn,"", "", "", date);
|
|
|
|
|
|
|
|
|
|
assertThrows(NotValidBookException.class,
|
|
|
|
|
() -> bookUseCase.registerBook(invalidBookInfo));
|
|
|
|
|
|
|
|
|
|
verify(bookRepository, never()).save(any(Book.class));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Nested
|
|
|
|
|
@DisplayName("Find book tests")
|
|
|
|
|
class FindBookTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should return book when isbn exists")
|
|
|
|
|
void testFindBookByIsbn() {
|
|
|
|
|
when(bookRepository.findByIsbn("1234567891012")).thenReturn(Optional.of(testBook));
|
|
|
|
|
|
|
|
|
|
Optional<BookDTO> foundBook = bookUseCase.findBookByIsbn("1234567891012");
|
|
|
|
|
|
|
|
|
|
assertTrue(foundBook.isPresent());
|
|
|
|
|
assertEquals(testBook.getIsbn(), foundBook.get().getIsbn());
|
|
|
|
|
assertEquals(testBook.getTitle(), foundBook.get().getTitle());
|
|
|
|
|
verify(bookRepository, times(1)).findByIsbn("1234567891012");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should return empty Optional when isbn doesn't exist")
|
|
|
|
|
void testFindBookByIsbn() {
|
|
|
|
|
when(bookRepository.findByIsbn("1656546262516")).thenReturn(Optional.empty());
|
|
|
|
|
|
|
|
|
|
Optional<BookDTO> foundBook = bookUseCase.findBookByIsbn("1656546262516");
|
|
|
|
|
|
|
|
|
|
assertTrue(foundBook.isEmpty());
|
|
|
|
|
verify(bookRepository, times(1)).findByIsbn("1656546262516");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Nested
|
|
|
|
|
@DisplayName("Update book tests")
|
|
|
|
|
class UpdateBookTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should update book when valid data is provided")
|
|
|
|
|
void testUpdateBookWithValidData() throws BookNotFoundException, NotValidBookException {
|
|
|
|
|
when(bookRepository.findByIsbn(bookIsbn)).thenReturn(Optional.of(testBook));
|
|
|
|
|
|
|
|
|
|
Book updatedBook = Book.builder()
|
|
|
|
|
.isbn(bookIsbn)
|
|
|
|
|
.title("La vie de Maxime")
|
|
|
|
|
.author("Updated")
|
|
|
|
|
.editor("Kioon")
|
|
|
|
|
.date(date)
|
|
|
|
|
.price(12.99)
|
|
|
|
|
.stock(50)
|
|
|
|
|
.categories(categories)
|
|
|
|
|
.description("C'était un brave partit trop tôt")
|
|
|
|
|
.language("Français")
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
when(bookRepository.save(any(Book.class))).thenReturn(updatedBook);
|
|
|
|
|
|
|
|
|
|
BookInfo updateInfo = new BookInfo(bookIsbn, "La vie de Maxime", "Updated", "Kioon", date);
|
|
|
|
|
BookSalesInfo updateSalesInfo = BookSalesInfo.builder()
|
|
|
|
|
.price(12.99)
|
|
|
|
|
.stock(50)
|
|
|
|
|
.build();
|
|
|
|
|
BookDetails updateDetails = BookDetails.builder()
|
|
|
|
|
.categories(categories)
|
|
|
|
|
.description("C'était un brave partit trop tôt")
|
|
|
|
|
.language("Français")
|
|
|
|
|
.build();
|
|
|
|
|
BookDTO result = BookUseCase.updateBook(updateInfo, updateSalesInfo, updateDetails);
|
|
|
|
|
|
|
|
|
|
assertNotNull(result);
|
|
|
|
|
assertEquals(bookIsbn, result.getIsbn());
|
|
|
|
|
assertEquals("La vie de Maxime", result.getTitle());
|
|
|
|
|
assertEquals("Updated", result.getAuthor());
|
|
|
|
|
assertEquals("Kioon", result.getEditor());
|
|
|
|
|
assertEquals(date, result.getDate());
|
|
|
|
|
assertEquals(12.99, result.getPrice());
|
|
|
|
|
assertEquals(50, result.getStock());
|
|
|
|
|
assertEquals(categories, result.getCategories());
|
|
|
|
|
assertEquals("C'était un brave partit trop tôt", result.getDescription());
|
|
|
|
|
assertEquals("Français", result.getLanguage());
|
|
|
|
|
verify(bookRepository, times(1)).findByIsbn(bookIsbn);
|
|
|
|
|
verify(bookRepository, times(1)).save(any(Book.class));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should throw exception when book isbn doesn't exist")
|
|
|
|
|
void testUpdateBookNotFound() {
|
|
|
|
|
String nonExistentIsbn = "1656546262516";
|
|
|
|
|
when(bookRepository.findByIsbn(nonExistentIsbn)).thenReturn(Optional.empty());
|
|
|
|
|
|
|
|
|
|
BookInfo updateInfo = new BookInfo(nonExistentIsbn, "La vie de Maxime", "Updated", "Kioon", date);
|
|
|
|
|
BookSalesInfo updateSalesInfo = BookSalesInfo.builder()
|
|
|
|
|
.price(12.99)
|
|
|
|
|
.stock(50)
|
|
|
|
|
.build();
|
|
|
|
|
BookDetails updateDetails = BookDetails.builder()
|
|
|
|
|
.categories(categories)
|
|
|
|
|
.description("C'était un brave partit trop tôt")
|
|
|
|
|
.language("Français")
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
assertThrows(BookNotFoundException.class,
|
|
|
|
|
() -> bookUseCase.updateBook(updateInfo, updateSalesInfo, updateDetails));
|
|
|
|
|
|
|
|
|
|
verify(bookRepository, times(1)).findByIsbn(nonExistentIsbn);
|
|
|
|
|
verify(bookRepository, never()).save(any(Book.class));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should throw exception when update data is not valid")
|
|
|
|
|
void testUpdateBookWithInvalidData() {
|
|
|
|
|
BookInfo invalidUpdateInfo = new BookInfo(bookIsbn,"", "", "", date);
|
|
|
|
|
BookSalesInfo invalidUpdateSalesInfo = BookSalesInfo.builder()
|
|
|
|
|
.price(0)
|
|
|
|
|
.stock(-3)
|
|
|
|
|
.build();
|
|
|
|
|
BookDetails invalidUpdateDetails = BookDetails.builder()
|
|
|
|
|
.categories(categories)
|
|
|
|
|
.description("")
|
|
|
|
|
.language("")
|
|
|
|
|
.build();
|
|
|
|
|
|
|
|
|
|
assertThrows(NotValidBookException.class,
|
|
|
|
|
() -> bookUseCase.updateBook(invalidUpdateInfo, invalidUpdateSalesInfo, invalidUpdateDetails));
|
|
|
|
|
|
|
|
|
|
verify(bookRepository, never()).findByIsbn(any(String.class));
|
|
|
|
|
verify(bookRepository, never()).save(any(Book.class));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Nested
|
|
|
|
|
@DisplayName("Delete book tests")
|
|
|
|
|
class DeleteBookTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should delete book when isbn exists")
|
|
|
|
|
void testDeleteBook() throws BookNotFoundException {
|
|
|
|
|
when(bookRepository.findByIsbn(bookIsbn)).thenReturn(Optional.of(testBook));
|
|
|
|
|
doNothing().when(bookRepository).delete(testBook);
|
|
|
|
|
|
|
|
|
|
bookUseCase.deleteBook(bookIsbn);
|
|
|
|
|
|
|
|
|
|
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() {
|
|
|
|
|
String nonExistentIsbn = "1656546262516";
|
|
|
|
|
when(bookRepository.findByIsbn(nonExistentIsbn)).thenReturn(Optional.empty());
|
|
|
|
|
|
|
|
|
|
assertThrows(BookNotFoundException.class,
|
|
|
|
|
() -> bookUseCase.deleteBook(nonExistentIsbn));
|
|
|
|
|
|
|
|
|
|
verify(bookRepository, times(1)).findByIsbn(nonExistentIsbn);
|
|
|
|
|
verify(bookRepository, never()).delete(any(Book.class));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Nested
|
|
|
|
|
@DisplayName("Stock copies tests")
|
|
|
|
|
class StockCopiesTests {
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should add stock copies to book")
|
|
|
|
|
void testAddStockCopies() throws BookNotFoundException {
|
|
|
|
|
when(bookRepository.findByIsbn(bookIsbn)).thenReturn(Optional.of(testBook));
|
|
|
|
|
when(bookRepository.save(testBook)).thenReturn(testBook);
|
|
|
|
|
|
|
|
|
|
int initialCopies = testBook.getStock();
|
|
|
|
|
int copiesToAdd = 50;
|
|
|
|
|
int expectedCopies = initialCopies + copiesToAdd;
|
|
|
|
|
|
|
|
|
|
int newCopies = bookUseCase.addStockCopies(bookIsbn, copiesToAdd);
|
|
|
|
|
|
|
|
|
|
assertEquals(expectedCopies, newCopies);
|
|
|
|
|
assertEquals(expectedCopies, testBook.getStock());
|
|
|
|
|
verify(bookRepository, times(1)).findByIsbn(bookIsbn);
|
|
|
|
|
verify(bookRepository, times(1)).save(testBook);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should throw exception when adding copies to non-existent book")
|
|
|
|
|
void testAddStockCopiesToNonExistentBook() {
|
|
|
|
|
String nonExistentIsbn = "1656546262516";
|
|
|
|
|
when(bookRepository.findByIsbn(nonExistentIsbn)).thenReturn(Optional.empty());
|
|
|
|
|
|
|
|
|
|
assertThrows(BookNotFoundException.class,
|
|
|
|
|
() -> bookUseCase.addStockCopies(nonExistentIsbn, 50));
|
|
|
|
|
|
|
|
|
|
verify(bookRepository, times(1)).findByIsbn(nonExistentIsbn);
|
|
|
|
|
verify(bookRepository, never()).save(any(Book.class));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should subtract stock copies from book")
|
|
|
|
|
void testSubtractStockCopies() throws BookNotFoundException, IllegalBookCpoiesException {
|
|
|
|
|
when(bookRepository.findByIsbn(bookIsbn)).thenReturn(Optional.of(testBook));
|
|
|
|
|
when(bookRepository.save(testBook)).thenReturn(testBook);
|
|
|
|
|
|
|
|
|
|
int initialCopies = testBook.getStock();
|
|
|
|
|
int copiesToRemove = 30;
|
|
|
|
|
int expectedCopies = initialCopies - copiesToRemove;
|
|
|
|
|
|
|
|
|
|
int newCopies = bookUseCase.subtractStockCopies(bookIsbn, copiesToRemove);
|
|
|
|
|
|
|
|
|
|
assertEquals(expectedCopies, newCopies);
|
|
|
|
|
assertEquals(expectedCopies, testBook.getStock());
|
|
|
|
|
verify(bookRepository, times(1)).findByIsbn(bookIsbn);
|
|
|
|
|
verify(bookRepository, times(1)).save(testBook);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should throw exception when trying to remove more copies than available")
|
|
|
|
|
void testSubtractTooManyStockCopies() {
|
|
|
|
|
when(bookRepository.findByIsbn(bookIsbn)).thenReturn(Optional.of(testBook));
|
|
|
|
|
|
|
|
|
|
int copiesToRemove = 200;
|
|
|
|
|
|
|
|
|
|
assertThrows(IllegalBookCpoiesException.class,
|
|
|
|
|
() -> bookUseCase.subtractStockCopies(bookIsbn, copiesToRemove));
|
|
|
|
|
|
|
|
|
|
verify(bookRepository, times(1)).findByIsbn(bookIsbn);
|
|
|
|
|
verify(bookRepository, never()).save(any(Book.class));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
@DisplayName("Should throw exception when subtracting copies from non-existent book")
|
|
|
|
|
void testSubtractStockCopiesFromNonExistentBook() {
|
|
|
|
|
String nonExistentIsbn = "1656546262516";
|
|
|
|
|
when(bookRepository.findByIsbn(nonExistentIsbn)).thenReturn(Optional.empty());
|
|
|
|
|
|
|
|
|
|
assertThrows(BookNotFoundException.class,
|
|
|
|
|
() -> bookUseCase.subtractStockCopies(nonExistentIsbn, 50));
|
|
|
|
|
|
|
|
|
|
verify(bookRepository, times(1)).findByIsbn(nonExistentIsbn);
|
|
|
|
|
verify(bookRepository, never()).save(any(Book.class));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|