Compare commits

...

13 Commits

53 changed files with 4005 additions and 0 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

7
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

12
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_23" default="true" project-jdk-name="openjdk-23" project-jdk-type="JavaSDK" />
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

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
@Getter
public class Customer {
private UUID id;
private String firstName;

View File

@@ -0,0 +1,13 @@
package fr.iut_fbleau.but3.dev62.mylibrary.order;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class AddressDTO {
private final String street;
private final String city;
private final String postalCode;
private final String country;
}

View File

@@ -0,0 +1,19 @@
package fr.iut_fbleau.but3.dev62.mylibrary.order;
import lombok.Builder;
import lombok.Getter;
import java.util.List;
import java.util.UUID;
@Builder
@Getter
public class OrderDTO {
private final UUID id;
private final UUID customerId;
private final List<OrderLineDTO> orderLines;
private final double totalPrice;
private final double totalPriceToPay;
private final AddressDTO address;
private final PaymentMethod paymentMethod;
}

View File

@@ -0,0 +1,11 @@
package fr.iut_fbleau.but3.dev62.mylibrary.order;
import lombok.Builder;
import lombok.Getter;
@Builder
@Getter
public class OrderLineDTO {
private final String bookId;
private final int quantity;
}

View File

@@ -0,0 +1,6 @@
package fr.iut_fbleau.but3.dev62.mylibrary.order;
public enum PaymentMethod {
CREDIT_CARD,
LOYALTY_POINTS
}

View File

@@ -0,0 +1,15 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription;
import lombok.Builder;
import lombok.Getter;
import java.util.UUID;
@Builder
@Getter
public class SubscriptionDTO {
private final UUID id;
private final UUID customerId;
private final Integer duration;
private final String paymentMethod;
private final String debutDate;
}

View File

@@ -0,0 +1,7 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription;
import java.util.UUID;
public record SubscriptionInfo(UUID customerId, Integer duration, String paymentMethod) {
}

View File

@@ -0,0 +1,31 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.converter;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionDTO;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionInfo;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity.Subscription;
public final class SubscriptionConverter {
private SubscriptionConverter() {
}
public static Subscription toDomain(SubscriptionInfo newSubscription) {
return Subscription.builder()
.customerId(newSubscription.customerId())
.duration(newSubscription.duration())
.paymentMethod(newSubscription.paymentMethod())
.build();
}
public static SubscriptionDTO toDTO(Subscription subscription) {
return SubscriptionDTO.builder()
.id(subscription.getId())
.customerId(subscription.getCustomerId())
.duration(subscription.getDuration())
.paymentMethod(subscription.getPaymentMethod())
.debutDate(subscription.getDebutDate())
.build();
}
}

View File

@@ -0,0 +1,24 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity;
import java.util.UUID;
import lombok.Builder;
import lombok.Getter;
import java.time.LocalDate;
@Builder
@Getter
public class Subscription {
private UUID id;
private UUID customerId;
private Integer duration;
private String paymentMethod;
private String debutDate;
public void setRandomUUID() {
this.id = UUID.randomUUID();
}
public void setDebutDate(String debutDate) {
this.debutDate = LocalDate.now().toString();
}
}

View File

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

View File

@@ -0,0 +1,12 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception;
import java.text.MessageFormat;
import java.util.UUID;
public class SubscriptionNotFoundException extends Exception {
public static final String THE_SUBSCRIPTION_WITH_ID_DOES_NOT_EXIST_MESSAGE = "The customer with id {0} does not exist";
public SubscriptionNotFoundException(UUID uuid) {
super(MessageFormat.format(THE_SUBSCRIPTION_WITH_ID_DOES_NOT_EXIST_MESSAGE, uuid));
}
}

View File

@@ -0,0 +1,42 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.repository;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity.Subscription;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import lombok.NoArgsConstructor;
public class SubscriptionRepository {
private final List<Subscription> subscriptions = new ArrayList<>();
public List<Subscription> findAll() {
return subscriptions;
}
public Subscription save(Subscription newSubscription) {
Optional<Subscription> optionalSubscriptionWithSameId = this.findByCustomerId(newSubscription.getCustomerId());
optionalSubscriptionWithSameId.ifPresentOrElse(subscriptions::remove, newSubscription::setRandomUUID);
this.subscriptions.add(newSubscription);
return newSubscription;
}
public Optional<Subscription> findByCustomerId(UUID uuid) {
return this.subscriptions.stream()
.filter(subscription -> subscription.getCustomerId().equals(uuid))
.findFirst();
}
public Optional<Subscription> findById(UUID id) {
return this.subscriptions.stream()
.filter(subscription -> subscription.getId().equals(id))
.findFirst();
}
public boolean existsById(UUID uuid) {
return this.subscriptions.stream()
.anyMatch(subscription -> subscription.getId().equals(uuid));
}
}

View File

@@ -0,0 +1,45 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.usecase;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionDTO;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionInfo;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.converter.SubscriptionConverter;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity.Subscription;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.SubscriptionNotFoundException;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.NotValidSubscriptionException;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.repository.SubscriptionRepository;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.validator.SubscriptionValidator;
import java.util.Optional;
import java.util.UUID;
public class SubscriptionUseCase {
private final SubscriptionRepository subscriptionRepository;
public SubscriptionUseCase(SubscriptionRepository subscriptionRepository) {
this.subscriptionRepository = subscriptionRepository;
}
public UUID registerSubscription(SubscriptionInfo newSubscription) throws NotValidSubscriptionException {
SubscriptionValidator.validate(newSubscription);
Subscription subscriptionToRegister = SubscriptionConverter.toDomain(newSubscription);
Subscription subscriptionToRegistered = subscriptionRepository.save(subscriptionToRegister);
if (subscriptionToRegistered.getDuration() <= 0) {
throw new NotValidSubscriptionException("Duration must be positive");
}
return subscriptionToRegistered.getId();
}
public Optional<SubscriptionDTO> findSubscriptionByCustomerId(UUID customerId) {
Optional<Subscription> optionalSubscription = subscriptionRepository.findByCustomerId(customerId);
return optionalSubscription.map(SubscriptionConverter::toDTO);
}
private boolean getSubscriptionIfDoesNotExistThrowSubscriptionNotFoundException(UUID uuid)
throws SubscriptionNotFoundException {
if (subscriptionRepository.existsById(uuid)) {
throw new SubscriptionNotFoundException(uuid);
}
return subscriptionRepository.existsById(uuid);
}
}

View File

@@ -0,0 +1,44 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.validator;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerInfo;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.NotValidCustomerException;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionDTO;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionInfo;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.NotValidSubscriptionException;
public class SubscriptionValidator {
public static final String CUSTOMER_ID_CANNOT_BE_NULL = "Customer ID cannot be null";
public static final String DURATION_CANNOT_BE_NULL = "Duration is not valid";
public static final String PAYMENT_METHOD_CANNOT_BE_BLANK = "Payment Method cannot be blank";
private SubscriptionValidator() {
}
public static void validate(SubscriptionInfo newSubscription) throws NotValidSubscriptionException {
validateCustomerId(newSubscription);
validateDuration(newSubscription);
validatePaymentMethod(newSubscription);
}
private static void validateCustomerId(SubscriptionInfo newSubscription) throws NotValidSubscriptionException {
if (newSubscription.customerId() == null) {
throw new NotValidSubscriptionException(CUSTOMER_ID_CANNOT_BE_NULL);
}
}
private static void validateDuration(SubscriptionInfo newSubscription) throws NotValidSubscriptionException {
if (newSubscription.duration() == null) {
throw new NotValidSubscriptionException(DURATION_CANNOT_BE_NULL);
}
}
private static void validatePaymentMethod(SubscriptionInfo newSubscription) throws NotValidSubscriptionException {
if (newSubscription.paymentMethod().isBlank()) {
throw new NotValidSubscriptionException(PAYMENT_METHOD_CANNOT_BE_BLANK);
}
}
}

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

@@ -0,0 +1,157 @@
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;
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.Instant;
import java.util.ArrayList;
import java.util.Date;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class BookUseCaseTest {
@Mock
private BookRepository bookRepository;
@InjectMocks
private BookUseCase bookUseCase;
private String bookISBN;
private Book testBook;
private BookInfo validBookInfo;
@BeforeEach
void setUp() {
ArrayList<String> 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")
.date(date)
.price(12.5)
.initialStock(50)
.categories(categories)
.description("Je suis un livre qui est composé de mots.")
.language("Francais")
.build();
validBookInfo = new BookInfo("bookISBN","LivreRandom", "John Doe", "RandomPublisher", date, 12.5, 50, categories, "Je suis un livre qui est composé de mots.", "Francais");
}
@Nested
@DisplayName("Register Book Tests")
public 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("", "", "", "", Date.from(Instant.now()),-78, -1, null, "", "");
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("1234567890123")).thenReturn(Optional.empty());
Optional<BookDTO> foundBook = bookUseCase.findBookByISBN("1234567890123");
assertTrue(foundBook.isPresent());
assertEquals(testBook.getIsbn(), foundBook.get().getIsbn());
assertEquals(testBook.getTitle(), foundBook.get().getTitle());
verify(bookRepository, times(1)).findByISBN("1234567890123");
}
@Test
@DisplayName("Should return empty Optional when ISBN doesn't exist")
void testFindBookByISBNNotFound() {
when(bookRepository.findByISBN("9999999999999")).thenReturn(Optional.empty());
Optional<BookDTO> foundBook = bookUseCase.findBookByISBN("9999999999999");
assertTrue(foundBook.isEmpty());
verify(bookRepository, times(1)).findByISBN("9999999999999");
}
}
@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 = "0000000000001";
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));
}
}
}

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

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,299 @@
package fr.iut_fbleau.but3.dev62.mylibrary.features.order;
import static org.junit.jupiter.api.Assertions.*;
import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderLineDTO;
import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderDTO;
import fr.iut_fbleau.but3.dev62.mylibrary.order.AddressDTO;
import fr.iut_fbleau.but3.dev62.mylibrary.order.OrderInfo;
import fr.iut_fbleau.but3.dev62.mylibrary.order.repository.OrderRepository;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.OrderNotFoundException;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.UserNotFoundException;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository;
import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository;
import io.cucumber.datatable.DataTable;
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.util.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class OrderSteps {
private final OrderRepository orderRepository = new OrderRepository();
private final OrderUserCase orderUserCase = new OrderUseCase(orderRepository);
private final CustomerRepository customerRepository = new CustomerRepository();
private final Map<String, UUID> customerPhoneUUID = new HashMap<>();
private final BookRepository bookRepository = new BookRepository();
private final Map<String, String> bookISBN = new HashMap<>();
private UUID orderRegistration;
private Optional<OrderDTO> orderByUUID;
private Book updatedBook;
private Customer updatedCustomer;
private Exception exception;
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("the system has the following books in stock:")
public void theSystemHasTheFollowingBooksInStock(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());
}
@And("the system has the following customers:")
public void theSystemHasTheFollowingCustomers(DataTable dataTable) {
int size = customerRepository.findAll().size();
if (size > 0) {
customerRepository.deleteAll();
}
List<Map<String, String>> customers = dataTable.asMaps(String.class, String.class);
for (Map<String, String> customer : customers) {
String numeroTelephone = customer.get("numeroTelephone");
Customer newCustomer = Customer.builder()
.firstName(customer.get("prenom"))
.lastName(customer.get("nom"))
.phoneNumber(numeroTelephone)
.loyaltyPoints(Integer.parseInt(customer.get("pointsFidelite")))
.build();
Customer save = customerRepository.save(newCustomer);
customerPhoneUUID.put(numeroTelephone, save.getId());
}
assertEquals(customers.size(), customerRepository.findAll().size());
}
@When("I create a new order with the following information:")
public void iCreateANewOrderWithTheFollowingInformation(DataTable dataTable) {
Map<String, String> orderData = dataTable.asMaps(String.class, String.class).getFirst();
OrderInfo newOrder = new OrderInfo(
UUID.fromString(orderData.get("customerId")),
orderData.get("paymentMethod")
);
try{
orderRegistration = orderUseCase.registerOrder(newOrder);
exception = null;
} catch (Exception e) {
exception = e;
orderRegistration = null;
}
}
@And("the order includes the following books:")
public void theOrderIncludesTheFollowingBooks(DataTable dataTable){
List<Map<String, String>> books = dataTable.asMaps(String.class, String.class);
List<OrderLineDTO> orderLines = new ArrayList<>();
for (Map<String, String> book : books) {
String isbn = book.get("ISBN");
int quantity = Integer.parseInt(book.get("quantity"));
orderLines.add(OrderLineDTO.builder()
.bookId(isbn)
.quantity(quantity)
.build());
}
try {
orderUseCase.addBooksToOrder(orderRegistration, orderLines);
exception = null;
} catch (Exception e) {
exception = e;
orderRegistration = null;
}
}
@And("the delivery address is:")
public void theDeliveryAddressIs(DataTable dataTable) {
Map<String, String> addressData = dataTable.asMaps(String.class, String.class).getFirst();
AddressDTO address = AddressDTO.builder()
.street(addressData.get("street"))
.city(addressData.get("city"))
.postalCode(addressData.get("postalCode"))
.country(addressData.get("country"))
.build();
try {
orderUseCase.setDeliveryAddress(orderRegistration, address);
exception = null;
} catch (Exception e) {
exception = e;
orderRegistration = null;
}
}
@Then("a new order is created")
public void aNewOrderIsCreated() {
assertNull(exception, "No exception should be thrown during order creation");
assertNotNull(orderRegistration);
}
@And("the total price is {double}")
public void theTotalPriceIs(double expectedPrice) {
Order order = orderRepository.findById(orderRegistration)
.orElseThrow(() -> new Exception("Order not found"));
double totalPrice = order.getTotalPrice();
assertEquals(expectedPrice, totalPrice, "The total price of the order should match the expected price");
}
@And("{int} loyalty points are deducted")
public void loyaltyPointsAreDeducted(int loyaltyPoints) {
Order order = orderRepository.findById(orderRegistration).orElseThrow();
Customer customer = customerRepository.findById(order.getCustomerId())
.orElseThrow(() -> new Exception("Customer not found"));
int remainingPoints = customer.getLoyaltyPoints() - loyaltyPoints;
assertEquals(remainingPoints, customer.getLoyaltyPoints(), "The customer's loyalty points should be deducted correctly");
}
@And("I receive an error for validation order message containing {string}")
public void iReceiveAnErrorForValidationOrderMessageContaining(String errorMessage) {
assertNotNull(exception, "An exception should be thrown during order creation");
assertInstanceOf(NotValidOrderException.class, exception, "The exception should be of type NotValidOrderException");
assertEquals(errorMessage, exception.getMessage(), "The error message should match the expected message");
}
@And("I receive an error for not found exception message containing {string}")
public void iReceiveAnErrorForNotFoundExceptionMessageContaining(String errorMessage) {
assertNotNull(exception, "An exception should be thrown during order retrieval");
assertInstanceOf(OrderNotFoundException.class, exception, "The exception should be of type OrderNotFoundException");
assertEquals(errorMessage, exception.getMessage(), "The error message should match the expected message");
}
@And("I receive an error for not found user exception message containing {string}")
public void iReceiveAnErrorForIllegalOrderExceptionMessageContaining(String errorMessage) {
assertNotNull(exception, "An exception should be thrown during user processing");
assertInstanceOf(UserNotFoundException.class, exception, "The exception should be of type UserNotFoundException");
assertEquals(errorMessage, exception.getMessage(), "The error message should match the expected message");
}
@And("the order includes no books")
public void theOrderIncludesNoBooks() {
List<OrderLineDTO> orderLines = new ArrayList<>();
try {
orderUseCase.addBooksToOrder(orderRegistration, orderLines);
exception = null;
} catch (Exception e) {
exception = e;
orderRegistration = null;
}
}
@Given("an order with ID {string} exists for customer {string}")
public void anOrderWithIDExistsForCustomer(String orderId, String customerId) {
UUID orderUUID = UUID.fromString(orderId);
UUID customerUUID = UUID.fromString(customerId);
Order order = Order.builder()
.id(orderUUID)
.customerId(customerUUID)
.orderLines(new ArrayList<OrderLineDTO>() {{
add(OrderLineDTO.builder()
.bookId("1234567890123")
.quantity(2)
.build());
add(OrderLineDTO.builder()
.bookId("9876543210987")
.quantity(1)
.build());
}})
.totalPrice(60.0)
.totalPriceToPay(60.0)
.address(AddressDTO.builder()
.street("123 Main St")
.city("Springfield")
.postalCode("12345")
.country("USA")
.build())
.paymentMethod("CREDIT_CARD")
.build();
orderRepository.save(order);
}
@When("I retrieve the order by ID {string}")
public void iRetrieveTheOrderByID(String orderId) {
UUID orderUUID = UUID.fromString(orderId);
try {
orderByUUID = orderUseCase.findOrderById(orderUUID);
exception = null;
} catch (Exception e) {
exception = e;
orderByUUID = Optional.empty();
}
}
@Then("I receive the order details")
public void iReceiveTheOrderDetails() {
assertTrue(orderByUUID.isPresent(), "The order should be found by ID");
OrderDTO order = orderByUUID.get();
assertNotNull(order, "The retrieved order should not be null");
assertEquals(orderRegistration, order.getId(), "The retrieved order ID should match the expected ID");
}
@When("I request all orders for customer {string}")
public void iRequestAllOrdersForCustomer(String customerId) {
UUID customerUUID = UUID.fromString(customerId);
List<OrderDTO> orders;
try {
orders = orderUseCase.findOrdersByCustomerId(customerUUID);
exception = null;
} catch (Exception e) {
exception = e;
orders = Collections.emptyList();
}
}
@Then("I receive a list of orders")
public void iReceiveAListOfOrders() {
assertNull(exception, "No exception should be thrown during order retrieval");
assertNotNull(orders, "The list of orders should not be null");
}
@Then("the retrieval fails")
public void theRetrievalFails() {
assertNotNull(exception, "An exception should be thrown during order retrieval");
}
}

View File

@@ -0,0 +1,173 @@
package fr.iut_fbleau.but3.dev62.mylibrary.features.subscription;
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.customer.exception.NotValidCustomerException;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.usecase.CustomerUseCase;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionInfo;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionDTO;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity.Subscription;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.NotValidSubscriptionException;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.SubscriptionNotFoundException;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.repository.SubscriptionRepository;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.usecase.SubscriptionUseCase;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository;
import io.cucumber.datatable.DataTable;
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.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.ArrayList;
public class SubscriptionSteps {
private final SubscriptionRepository subscriptionRepository = new SubscriptionRepository();
private final SubscriptionUseCase subscriptionUseCase = new SubscriptionUseCase(subscriptionRepository);
private NotValidSubscriptionException notValidSubscriptionException;
private SubscriptionNotFoundException subscriptionNotFoundException;
private static final List<Subscription> subscriptions = new ArrayList<>();
private final CustomerRepository customerRepository = new CustomerRepository();
private UUID subscriptionRegistration;
private final Map<String, UUID> customerPhoneUUID = new HashMap<>();
@Given("the system has the following customers:")
public void theSystemHasTheFollowingCustomers(DataTable dataTable) {
int size = customerRepository.findAll().size();
if (size > 0) {
customerRepository.deleteAll();
}
List<Map<String, String>> customers = dataTable.asMaps(String.class, String.class);
for (Map<String, String> customer : customers) {
String numeroTelephone = customer.get("numeroTelephone");
Customer newCustomer = Customer.builder()
.firstName(customer.get("prenom"))
.lastName(customer.get("nom"))
.phoneNumber(numeroTelephone)
.loyaltyPoints(Integer.parseInt(customer.get("pointsFidelite")))
.build();
Customer save = customerRepository.save(newCustomer);
customerPhoneUUID.put(numeroTelephone, save.getId());
}
assertEquals(customers.size(), customerRepository.findAll().size());
}
@When("I create a new subscription with CB:")
public void iCreateANewSubscriptionWithCB(DataTable dataTable) throws NotValidSubscriptionException {
List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
// Extract the first row of data
Map<String, String> subscriptionData = rows.getFirst();
// Create a new SubscriptionInfo object with the correct keys
SubscriptionInfo newSubscription = new SubscriptionInfo(
UUID.fromString(subscriptionData.get("customerId")),
Integer.parseInt(subscriptionData.get("duration")),
subscriptionData.get("paymentMethod")
);
// Register the subscription
subscriptionRegistration = subscriptionUseCase.registerSubscription(newSubscription);
}
@Then("a new subscription is created")
public void aNewSubscriptionIsCreated() {
assertNotNull(subscriptionRegistration);
assertTrue(subscriptionRepository.existsById(subscriptionRegistration));
}
@When("I create a new subscription with Paypal:")
public void iCreateANewSubscriptionWithPaypal(DataTable dataTable) throws NotValidSubscriptionException {
List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
// Extract the first row of data
Map<String, String> subscriptionData = rows.getFirst();
// Create a new SubscriptionInfo object with the correct keys
SubscriptionInfo newSubscription = new SubscriptionInfo(
UUID.fromString(subscriptionData.get("customerId")),
Integer.parseInt(subscriptionData.get("duration")),
subscriptionData.get("paymentMethod")
);
// Register the subscription
subscriptionRegistration = subscriptionUseCase.registerSubscription(newSubscription);
}
@When("I try to create a new subscription with the following information:")
public void iTryToCreateANewSubscriptionWithTheFollowingInformation(DataTable dataTable) throws NotValidSubscriptionException {
List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
Map<String, String> subscriptionData = rows.getFirst();
SubscriptionInfo newSubscription = new SubscriptionInfo(
UUID.fromString(subscriptionData.get("customerId")),
Integer.parseInt(subscriptionData.get("duration")),
subscriptionData.get("paymentMethod")
);
notValidSubscriptionException = assertThrows(NotValidSubscriptionException.class, () -> subscriptionUseCase.registerSubscription(newSubscription));
}
@Then("the subsription duration creation fails")
public void theSubsriptionDurationCreationFails() {
assertNotNull(notValidSubscriptionException);
}
@And("I receive an error for validation subscription message containing {string}")
public void iReceiveAnErrorForValidationSubscriptionMessageContaining(String errorMessage) {
assertEquals(errorMessage, notValidSubscriptionException.getMessage());
}
@When("I try to get a subscription with the following customerId:")
public void iTryToGetASubscriptionWithTheFollowingCustomerId(DataTable dataTable) {
List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
Map<String, String> subscriptionData = rows.getFirst();
UUID customerId = UUID.fromString(subscriptionData.get("customerId"));
try {
subscriptionRegistration = subscriptionUseCase.findSubscriptionByCustomerId(customerId)
.orElseThrow(() -> new SubscriptionNotFoundException(customerId))
.getId();
} catch (SubscriptionNotFoundException e) {
subscriptionNotFoundException = e;
}
}
@Then("I receive the following subscription:")
public void iReceiveTheFollowingSubscription(DataTable dataTable) {
List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class);
Map<String, String> expectedSubscription = rows.getFirst();
SubscriptionDTO subscription = subscriptionUseCase.findSubscriptionByCustomerId(
UUID.fromString(expectedSubscription.get("customerId"))
).orElseThrow(() -> new IllegalArgumentException("Subscription not found"));
assertEquals(UUID.fromString(expectedSubscription.get("customerId")), subscription.getCustomerId());
assertEquals(Integer.parseInt(expectedSubscription.get("duration")), subscription.getDuration());
assertEquals(expectedSubscription.get("paymentMethod"), subscription.getPaymentMethod());
}
@Then("I receive an error for not found subscription")
public void iReceiveAnErrorForNotFoundSubscription() {
assertNotNull(subscriptionNotFoundException);
}
}

View File

@@ -0,0 +1,265 @@
package fr.iut_fbleau.but3.dev62.mylibrary.order.entity;
import fr.iut_fbleau.but3.dev62.mylibrary.order.*;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.OrderNotFoundException;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.UserNotFoundException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
class OrderTest {
@Test
@DisplayName("Builder should create a valid Order instance")
void testOrderBuilder() {
UUID id = UUID.randomUUID();
UUID customerId = UUID.randomUUID();
List<OrderLineDTO> orderLines = List.of(
OrderLineDTO.builder().bookId("1234567890123").quantity(2).build(),
OrderLineDTO.builder().bookId("9876543210123").quantity(1).build()
);
double totalPrice = 89.98;
double totalPriceToPay = 89.98;
AddressDTO address = AddressDTO.builder()
.street("12 Main St.")
.city("Paris")
.postalCode("75000")
.country("France")
.build();
PaymentMethod paymentMethod = PaymentMethod.CREDIT_CARD;
OrderDTO order = OrderDTO.builder()
.id(id)
.customerId(customerId)
.orderLines(orderLines)
.totalPrice(totalPrice)
.totalPriceToPay(totalPriceToPay)
.address(address)
.paymentMethod(paymentMethod)
.build();
assertEquals(id, order.getId());
assertEquals(customerId, order.getCustomerId());
assertEquals(orderLines, order.getOrderLines());
assertEquals(totalPrice, order.getTotalPrice());
assertEquals(totalPriceToPay, order.getTotalPriceToPay());
assertEquals(address, order.getAddress());
assertEquals(paymentMethod, order.getPaymentMethod());
}
@Nested
@DisplayName("Order business logic tests")
class OrderBusinessLogicTests {
@Test
@DisplayName("Total price should be the sum of order lines")
void testTotalPriceCalculation() {
List<OrderLineDTO> orderLines = List.of(
OrderLineDTO.builder().bookId("1234567890123").quantity(2).build(),
OrderLineDTO.builder().bookId("9876543210123").quantity(1).build()
);
// Supposons prix 39.99 et 49.99
double price1 = 39.99;
double price2 = 49.99;
double expectedTotal = 2 * price1 + 1 * price2;
// Ici, on simule la logique métier (à adapter selon l'implémentation réelle)
double total = 2 * price1 + 1 * price2;
assertEquals(expectedTotal, total);
}
@Test
@DisplayName("Order with no lines should be invalid")
void testOrderWithNoLines() {
List<OrderLineDTO> emptyLines = List.of();
assertTrue(emptyLines.isEmpty(), "La liste des lignes de commande doit être vide");
}
}
@Test
@DisplayName("Delivery address should be properly set")
void testOrderAddress() {
AddressDTO address = AddressDTO.builder()
.street("42 Book Street")
.city("Lyon")
.postalCode("69000")
.country("France")
.build();
assertEquals("42 Book Street", address.getStreet());
assertEquals("Lyon", address.getCity());
assertEquals("69000", address.getPostalCode());
assertEquals("France", address.getCountry());
}
@Test
@DisplayName("Payment method should be correct")
void testOrderPaymentMethod() {
PaymentMethod method = PaymentMethod.LOYALTY_POINTS;
assertEquals(PaymentMethod.LOYALTY_POINTS, method);
}
@Nested
@DisplayName("Order business rules and validation")
class OrderBusinessRules {
@Test
@DisplayName("Create order with credit card - success")
void testCreateOrderWithCreditCard() {
// Arrange
UUID customerId = UUID.fromString("11111111-1111-1111-1111-111111111111");
List<OrderLineDTO> orderLines = List.of(
OrderLineDTO.builder().bookId("1234567890123").quantity(2).build()
);
AddressDTO address = AddressDTO.builder()
.street("12 Main St.").city("Paris").postalCode("75000").country("France").build();
double expectedTotal = 79.98;
// Act
OrderDTO order = OrderDTO.builder()
.id(UUID.randomUUID())
.customerId(customerId)
.orderLines(orderLines)
.totalPrice(expectedTotal)
.totalPriceToPay(expectedTotal)
.address(address)
.paymentMethod(PaymentMethod.CREDIT_CARD)
.build();
// Assert
assertEquals(expectedTotal, order.getTotalPrice());
assertEquals(PaymentMethod.CREDIT_CARD, order.getPaymentMethod());
assertEquals(customerId, order.getCustomerId());
}
@Test
@DisplayName("Create order with loyalty points - success")
void testCreateOrderWithLoyaltyPoints() {
UUID customerId = UUID.fromString("11111111-1111-1111-1111-111111111111");
List<OrderLineDTO> orderLines = List.of(
OrderLineDTO.builder().bookId("9876543210123").quantity(1).build()
);
AddressDTO address = AddressDTO.builder()
.street("42 Book Street").city("Lyon").postalCode("69000").country("France").build();
double expectedTotal = 49.99;
OrderDTO order = OrderDTO.builder()
.id(UUID.randomUUID())
.customerId(customerId)
.orderLines(orderLines)
.totalPrice(expectedTotal)
.totalPriceToPay(0.0)
.address(address)
.paymentMethod(PaymentMethod.LOYALTY_POINTS)
.build();
assertEquals(expectedTotal, order.getTotalPrice());
assertEquals(PaymentMethod.LOYALTY_POINTS, order.getPaymentMethod());
}
@Test
@DisplayName("Order with multiple books - total price calculation")
void testOrderWithMultipleBooks() {
List<OrderLineDTO> orderLines = List.of(
OrderLineDTO.builder().bookId("1234567890123").quantity(3).build(),
OrderLineDTO.builder().bookId("9876543210123").quantity(4).build()
);
double price1 = 39.99, price2 = 49.99;
double expectedTotal = 3 * price1 + 4 * price2;
double total = 3 * price1 + 4 * price2;
assertEquals(expectedTotal, total);
}
@Test
@DisplayName("Order with invalid address - should throw NotValidOrderException")
void testOrderWithInvalidAddress() {
AddressDTO address = AddressDTO.builder().street("").city("").postalCode("").country("").build();
Exception exception = assertThrows(NotValidOrderException.class, () -> {
if (address.getStreet().isEmpty() || address.getCity().isEmpty() || address.getPostalCode().isEmpty() || address.getCountry().isEmpty()) {
throw new NotValidOrderException("Address fields are required");
}
});
assertTrue(exception.getMessage().contains("Address fields are required"));
}
@Test
@DisplayName("Order with unknown customer - should throw UserNotFoundException")
void testOrderWithUnknownCustomer() {
UUID unknownCustomerId = UUID.fromString("00000000-0000-0000-0000-000000000000");
Exception exception = assertThrows(UserNotFoundException.class, () -> {
throw new UserNotFoundException("Customer not found");
});
assertTrue(exception.getMessage().contains("Customer not found"));
}
@Test
@DisplayName("Order with insufficient loyalty points - should throw NotValidOrderException")
void testOrderWithInsufficientLoyaltyPoints() {
int availablePoints = 10;
double orderPrice = 49.99;
Exception exception = assertThrows(NotValidOrderException.class, () -> {
if (orderPrice > availablePoints) {
throw new NotValidOrderException("Not enough loyalty points");
}
});
assertTrue(exception.getMessage().contains("Not enough loyalty points"));
}
@Test
@DisplayName("Order with quantity greater than stock - should throw NotValidOrderException")
void testOrderWithInsufficientStock() {
int stock = 10;
int requested = 50;
Exception exception = assertThrows(NotValidOrderException.class, () -> {
if (requested > stock) {
throw new NotValidOrderException("Insufficient book stock");
}
});
assertTrue(exception.getMessage().contains("Insufficient book stock"));
}
@Test
@DisplayName("Order with invalid payment method - should throw NotValidOrderException")
void testOrderWithInvalidPaymentMethod() {
String invalidMethod = "UNKNOWN";
Exception exception = assertThrows(NotValidOrderException.class, () -> {
try {
PaymentMethod.valueOf(invalidMethod);
} catch (IllegalArgumentException e) {
throw new NotValidOrderException("Payment method is not valid");
}
});
assertTrue(exception.getMessage().contains("Payment method is not valid"));
}
@Test
@DisplayName("Order with unknown book - should throw OrderNotFoundException")
void testOrderWithUnknownBook() {
String unknownBookId = "unknownBookId";
Exception exception = assertThrows(OrderNotFoundException.class, () -> {
throw new OrderNotFoundException("Book not found");
});
assertTrue(exception.getMessage().contains("Book not found"));
}
@Test
@DisplayName("Order with empty book list - should throw NotValidOrderException")
void testOrderWithEmptyBookList() {
List<OrderLineDTO> emptyLines = List.of();
Exception exception = assertThrows(NotValidOrderException.class, () -> {
if (emptyLines.isEmpty()) {
throw new NotValidOrderException("Book list cannot be empty");
}
});
assertTrue(exception.getMessage().contains("Book list cannot be empty"));
}
@Test
@DisplayName("Order with negative quantity - should throw NotValidOrderException")
void testOrderWithNegativeQuantity() {
int quantity = -1;
Exception exception = assertThrows(NotValidOrderException.class, () -> {
if (quantity < 0) {
throw new NotValidOrderException("Quantity must be positive");
}
});
assertTrue(exception.getMessage().contains("Quantity must be positive"));
}
}
}

View File

@@ -0,0 +1,57 @@
package fr.iut_fbleau.but3.dev62.mylibrary.order.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 NotValidOrderExceptionTest {
@Test
@DisplayName("Exception should be created with the provided message")
void testExceptionCreation() {
String errorMessage = "Order data is not valid";
NotValidOrderException exception = new NotValidOrderException(errorMessage);
assertEquals(errorMessage, exception.getMessage());
}
@ParameterizedTest
@ValueSource(strings = {
"Address fields are required",
"Book list cannot be empty",
"Quantity must be positive",
"Not enough loyalty points",
"Insufficient book stock",
"Payment method is not valid"
})
@DisplayName("Exception should handle different validation messages")
void testExceptionWithDifferentMessages(String errorMessage) {
NotValidOrderException exception = new NotValidOrderException(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(NotValidOrderException.class, () -> {
throw new NotValidOrderException(errorMessage);
});
assertEquals(errorMessage, exception.getMessage());
}
@Test
@DisplayName("Exception should be catchable as a general Exception")
void testExceptionInheritance() {
String errorMessage = "Invalid order data";
try {
throw new NotValidOrderException(errorMessage);
} catch (Exception e) {
assertEquals(NotValidOrderException.class, e.getClass());
assertEquals(errorMessage, e.getMessage());
}
}
}

View File

@@ -0,0 +1,43 @@
package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class OrderNotFoundExceptionTest {
@Test
@DisplayName("Exception message should contain the UUID provided")
void testExceptionMessageContainsUUID() {
UUID uuid = UUID.randomUUID();
OrderNotFoundException exception = new OrderNotFoundException(uuid);
String expectedMessage = String.format("The order with id %s does not exist", uuid);
assertEquals(expectedMessage, exception.getMessage());
}
@Test
@DisplayName("Exception should use the correct constant message format")
void testExceptionUsesConstantMessageFormat() {
UUID uuid = UUID.randomUUID();
OrderNotFoundException exception = new OrderNotFoundException(uuid);
String expectedFormatWithPlaceholder = "The order with id {0} does not exist";
assertEquals(OrderNotFoundException.THE_ORDER_WITH_ID_DOES_NOT_EXIST_MESSAGE, expectedFormatWithPlaceholder);
assertTrue(exception.getMessage().contains(uuid.toString()));
}
@Test
@DisplayName("Exception should be properly thrown and caught")
void testExceptionCanBeThrownAndCaught() {
UUID uuid = UUID.randomUUID();
try {
throw new OrderNotFoundException(uuid);
} catch (OrderNotFoundException e) {
String expectedMessage = String.format("The order with id %s does not exist", uuid);
assertEquals(expectedMessage, e.getMessage());
}
}
}

View File

@@ -0,0 +1,43 @@
package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class UserNotFoundExceptionTest {
@Test
@DisplayName("Exception message should contain the UUID provided")
void testExceptionMessageContainsUUID() {
UUID uuid = UUID.randomUUID();
UserNotFoundException exception = new UserNotFoundException(uuid);
String expectedMessage = String.format("The customer with id %s does not exist", uuid);
assertEquals(expectedMessage, exception.getMessage());
}
@Test
@DisplayName("Exception should use the correct constant message format")
void testExceptionUsesConstantMessageFormat() {
UUID uuid = UUID.randomUUID();
UserNotFoundException exception = new UserNotFoundException(uuid);
String expectedFormatWithPlaceholder = "The customer with id {0} does not exist";
assertEquals(UserNotFoundException.THE_CUSTOMER_WITH_ID_DOES_NOT_EXIST_MESSAGE, expectedFormatWithPlaceholder);
assertTrue(exception.getMessage().contains(uuid.toString()));
}
@Test
@DisplayName("Exception should be properly thrown and caught")
void testExceptionCanBeThrownAndCaught() {
UUID uuid = UUID.randomUUID();
try {
throw new UserNotFoundException(uuid);
} catch (UserNotFoundException e) {
String expectedMessage = String.format("The customer with id %s does not exist", uuid);
assertEquals(expectedMessage, e.getMessage());
}
}
}

View File

@@ -0,0 +1,195 @@
package fr.iut_fbleau.but3.dev62.mylibrary.order.repository;
import fr.iut_fbleau.but3.dev62.mylibrary.order.*;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.OrderNotFoundException;
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.UserNotFoundException;
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.util.*;
import static org.junit.jupiter.api.Assertions.*;
class OrderRepositoryTest {
private OrderRepository repository;
private OrderDTO order1;
private OrderDTO order2;
private UUID customerId1;
private UUID customerId2;
@BeforeEach
void setUp() {
repository = new OrderRepository();
customerId1 = UUID.fromString("11111111-1111-1111-1111-111111111111");
customerId2 = UUID.fromString("22222222-2222-2222-2222-222222222222");
order1 = OrderDTO.builder()
.id(UUID.randomUUID())
.customerId(customerId1)
.orderLines(List.of(OrderLineDTO.builder().bookId("1234567890123").quantity(2).build()))
.totalPrice(79.98)
.totalPriceToPay(79.98)
.address(AddressDTO.builder().street("12 Main St.").city("Paris").postalCode("75000").country("France").build())
.paymentMethod(PaymentMethod.CREDIT_CARD)
.build();
order2 = OrderDTO.builder()
.id(UUID.randomUUID())
.customerId(customerId2)
.orderLines(List.of(OrderLineDTO.builder().bookId("9876543210123").quantity(1).build()))
.totalPrice(49.99)
.totalPriceToPay(0.0)
.address(AddressDTO.builder().street("42 Book Street").city("Lyon").postalCode("69000").country("France").build())
.paymentMethod(PaymentMethod.LOYALTY_POINTS)
.build();
}
@Test
@DisplayName("New repository should be empty")
void testNewRepositoryIsEmpty() {
List<OrderDTO> orders = repository.findAll();
assertTrue(orders.isEmpty());
assertEquals(0, orders.size());
}
@Nested
@DisplayName("Save operations")
class SaveOperations {
@Test
@DisplayName("Save should add a new order")
void testSaveNewOrder() {
OrderDTO savedOrder = repository.save(order1);
assertEquals(1, repository.findAll().size());
assertEquals(order1.getId(), savedOrder.getId());
assertEquals(order1.getCustomerId(), savedOrder.getCustomerId());
}
@Test
@DisplayName("Save multiple orders should add all of them")
void testSaveMultipleOrders() {
repository.save(order1);
repository.save(order2);
List<OrderDTO> orders = repository.findAll();
assertEquals(2, orders.size());
assertTrue(orders.contains(order1));
assertTrue(orders.contains(order2));
}
}
@Nested
@DisplayName("Find operations")
class FindOperations {
@BeforeEach
void setUpOrders() {
repository.save(order1);
repository.save(order2);
}
@Test
@DisplayName("FindById should return order with matching ID")
void testFindById() {
Optional<OrderDTO> foundOrder = repository.findById(order1.getId());
assertTrue(foundOrder.isPresent());
assertEquals(order1.getCustomerId(), foundOrder.get().getCustomerId());
}
@Test
@DisplayName("FindById should throw OrderNotFoundException when ID doesn't exist")
void testFindByIdNotFound() {
UUID nonExistentId = UUID.randomUUID();
assertThrows(OrderNotFoundException.class, () -> {
repository.findByIdOrThrow(nonExistentId);
});
}
@Test
@DisplayName("FindByCustomerId should return all orders for a customer")
void testFindByCustomerId() {
List<OrderDTO> orders = repository.findByCustomerId(customerId1);
assertFalse(orders.isEmpty());
assertTrue(orders.stream().allMatch(o -> o.getCustomerId().equals(customerId1)));
}
@Test
@DisplayName("FindByCustomerId should throw UserNotFoundException when customer doesn't exist")
void testFindByCustomerIdNotFound() {
UUID nonExistentCustomer = UUID.randomUUID();
assertThrows(UserNotFoundException.class, () -> {
repository.findByCustomerIdOrThrow(nonExistentCustomer);
});
}
}
@Nested
@DisplayName("Validation and error handling")
class ValidationAndErrorHandling {
@Test
@DisplayName("Save should throw NotValidOrderException for invalid address")
void testSaveInvalidAddress() {
OrderDTO invalidOrder = OrderDTO.builder()
.id(UUID.randomUUID())
.customerId(customerId1)
.orderLines(List.of(OrderLineDTO.builder().bookId("1234567890123").quantity(1).build()))
.totalPrice(39.99)
.totalPriceToPay(39.99)
.address(AddressDTO.builder().street("").city("").postalCode("").country("").build())
.paymentMethod(PaymentMethod.CREDIT_CARD)
.build();
assertThrows(NotValidOrderException.class, () -> {
repository.saveOrThrow(invalidOrder);
});
}
@Test
@DisplayName("Save should throw NotValidOrderException for empty book list")
void testSaveEmptyBookList() {
OrderDTO invalidOrder = OrderDTO.builder()
.id(UUID.randomUUID())
.customerId(customerId1)
.orderLines(List.of())
.totalPrice(0.0)
.totalPriceToPay(0.0)
.address(AddressDTO.builder().street("12 Main St.").city("Paris").postalCode("75000").country("France").build())
.paymentMethod(PaymentMethod.CREDIT_CARD)
.build();
assertThrows(NotValidOrderException.class, () -> {
repository.saveOrThrow(invalidOrder);
});
}
@Test
@DisplayName("Save should throw NotValidOrderException for negative quantity")
void testSaveNegativeQuantity() {
OrderDTO invalidOrder = OrderDTO.builder()
.id(UUID.randomUUID())
.customerId(customerId1)
.orderLines(List.of(OrderLineDTO.builder().bookId("1234567890123").quantity(-1).build()))
.totalPrice(-39.99)
.totalPriceToPay(-39.99)
.address(AddressDTO.builder().street("12 Main St.").city("Paris").postalCode("75000").country("France").build())
.paymentMethod(PaymentMethod.CREDIT_CARD)
.build();
assertThrows(NotValidOrderException.class, () -> {
repository.saveOrThrow(invalidOrder);
});
}
@Test
@DisplayName("Save should throw NotValidOrderException for null payment method")
void testSaveNullPaymentMethod() {
OrderDTO invalidOrder = OrderDTO.builder()
.id(UUID.randomUUID())
.customerId(customerId1)
.orderLines(List.of(OrderLineDTO.builder().bookId("1234567890123").quantity(1).build()))
.totalPrice(39.99)
.totalPriceToPay(39.99)
.address(AddressDTO.builder().street("12 Main St.").city("Paris").postalCode("75000").country("France").build())
.paymentMethod(null) // Invalid payment method
.build();
assertThrows(NotValidOrderException.class, () -> {
repository.saveOrThrow(invalidOrder);
});
}
}
}

View File

@@ -0,0 +1,103 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.converter;
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.converter.CustomerConverter;
import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionInfo;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionDTO;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity.Subscription;
import java.util.UUID;
import java.util.concurrent.Flow;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@DisplayName("SubscriptionConverter Unit Tests")
public class SubscriptionConverterTest {
@Nested
@DisplayName("toDomain() method tests")
public class toDomainTests {
@Test
@DisplayName("Should convert SubscriptionInfo to Subscription domain object")
void shouldConvertSubscriptionInfoToDomain() {
// Given
SubscriptionInfo subscriptionInfo = new SubscriptionInfo(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"), 12, "CB");
// When
Subscription result = SubscriptionConverter.toDomain(subscriptionInfo);
// Then
assertNotNull(result);
assertEquals(subscriptionInfo.customerId(), result.getCustomerId());
assertEquals(subscriptionInfo.duration(), result.getDuration());
assertEquals(subscriptionInfo.paymentMethod(), result.getPaymentMethod());
}
}
@Nested
@DisplayName("toDTO() method tests")
public class toDTOTests {
@Test
@DisplayName("Should convert Subscriber domain object to SubscriberDTO with all fields mapped correctly")
void shouldConvertSubscriptionToDTO() {
UUID id = UUID.randomUUID();
Subscription subscription = Subscription.builder()
.id(UUID.randomUUID())
.customerId(id)
.duration(12)
.paymentMethod("CB")
.debutDate("2025-06-08")
.build();
SubscriptionDTO result = SubscriptionConverter.toDTO(subscription);
assertNotNull(result);
assertEquals(subscription.getId(), result.getId());
assertEquals(subscription.getCustomerId(), result.getCustomerId());
assertEquals(subscription.getDuration(), result.getDuration());
assertEquals(subscription.getPaymentMethod(), result.getPaymentMethod());
assertEquals(subscription.getDebutDate(), result.getDebutDate());
}
@Test
@DisplayName("Should handle null values properly when converting between objects")
void shouldHandleNullValuesGracefully() {
Subscription subscription = Subscription.builder()
.id(UUID.randomUUID())
.customerId(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"))
.duration(null)
.paymentMethod("NullTest")
.debutDate("2025-06-08")
.build();
SubscriptionDTO result = SubscriptionConverter.toDTO(subscription);
assertNotNull(result);
assertNull(result.getDuration());
assertEquals("NullTest", result.getPaymentMethod());
}
@Test
@DisplayName("Should preserve empty string values during conversion")
void shouldPreserveEmptyStrings() {
SubscriptionInfo subscriptionInfo = new SubscriptionInfo(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"), 12, "");
Subscription domainResult = SubscriptionConverter.toDomain(subscriptionInfo);
SubscriptionDTO dtoResult = SubscriptionConverter.toDTO(domainResult);
assertEquals("", dtoResult.getPaymentMethod());
}
}
}

View File

@@ -0,0 +1,49 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity;
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.util.UUID;
public class SubscriptionTest {
@Test
@DisplayName("Builder should create a valid Subscription instance")
void testSubscriptionBuilder() {
UUID id = UUID.randomUUID();
UUID customerId = UUID.fromString("123e4567-e89b-12d3-a456-426614174000");
int duration = 12;
String paymentMethod = "CB";
String debutDate = "2025-06-08";
Subscription subscription = Subscription.builder()
.id(id)
.customerId(customerId)
.duration(duration)
.paymentMethod(paymentMethod)
.debutDate(debutDate)
.build();
assertEquals(id, subscription.getId());
assertEquals(customerId, subscription.getCustomerId());
assertEquals(duration, subscription.getDuration());
assertEquals(paymentMethod, subscription.getPaymentMethod());
assertEquals(debutDate, subscription.getDebutDate());
}
@Test
@DisplayName("setRandomUUID should change the ID to a new random UUID")
void testSetRandomUUID() {
Subscription subscription = Subscription.builder().build();
UUID originalId = subscription.getId();
subscription.setRandomUUID();
assertNotNull(subscription.getId());
assertNotEquals(originalId, subscription.getId());
}
}

View File

@@ -0,0 +1,155 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.repository;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity.Subscription;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.repository.SubscriptionRepository;
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.util.List;
import java.util.Optional;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.*;
public class SubscriptionRepositoryTest {
private SubscriptionRepository repository;
private Subscription Subscription1;
private Subscription Subscription2;
@BeforeEach
void setUp() {
repository = new SubscriptionRepository();
Subscription1 = Subscription.builder()
.customerId(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"))
.duration(12)
.paymentMethod("CB")
.build();
Subscription1.setRandomUUID();
Subscription2 = Subscription.builder()
.customerId(UUID.fromString("456e4567-e89b-12d3-a456-426614174000"))
.duration(24)
.paymentMethod("Paypal")
.build();
Subscription2.setRandomUUID();
}
@Test
@DisplayName("New repository should be empty")
void testNewRepositoryIsEmpty() {
List<Subscription> subscriptions = repository.findAll();
assertTrue(subscriptions.isEmpty());
assertEquals(0, subscriptions.size());
}
@Nested
@DisplayName("Save operations")
class SaveOperations {
@Test
@DisplayName("Save should add a new subsciption")
void testSaveNewSubscription() {
Subscription savedSubscription = repository.save(Subscription1);
assertEquals(1, repository.findAll().size());
assertEquals(Subscription1.getId(), savedSubscription.getId());
assertEquals(Subscription1.getDebutDate(), savedSubscription.getDebutDate());
}
@Test
@DisplayName("Save multiple Subscriptions should add all of them")
void testSaveMultipleSubscriptions() {
repository.save(Subscription1);
repository.save(Subscription2);
List<Subscription> subscriptions = repository.findAll();
assertEquals(2, subscriptions.size());
assertTrue(subscriptions.contains(Subscription1));
assertTrue(subscriptions.contains(Subscription2));
}
}
@Nested
@DisplayName("Find operations")
class FindOperations {
@BeforeEach
void setUpSubscriptions() {
repository.save(Subscription1);
repository.save(Subscription2);
}
@Test
@DisplayName("FindAll should return all subscriptions")
void testFindAll() {
List<Subscription> subscriptions = repository.findAll();
assertEquals(2, subscriptions.size());
assertTrue(subscriptions.contains(Subscription1));
assertTrue(subscriptions.contains(Subscription2));
}
@Test
@DisplayName("FindById should return subscriptions with matching ID")
void testFindById() {
Optional<Subscription> foundSubscription = repository.findById(Subscription1.getId());
assertTrue(foundSubscription.isPresent());
assertEquals(Subscription1.getId(), foundSubscription.get().getId());
assertEquals(Subscription1.getCustomerId(), foundSubscription.get().getCustomerId());
}
@Test
@DisplayName("FindById should return empty Optional when ID doesn't exist")
void testFindByIdNotFound() {
UUID nonExistentId = UUID.randomUUID();
Optional<Subscription> foundSubscription = repository.findByCustomerId(nonExistentId);
assertTrue(foundSubscription.isEmpty());
}
@Test
@DisplayName("FindByCustomerId should return customer with matching customer id")
void testFindByCustomerId() {
Optional<Subscription> foundSubscription = repository.findByCustomerId(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"));
assertTrue(foundSubscription.isPresent());
assertEquals(Subscription1.getId(), foundSubscription.get().getId());
assertEquals(Subscription1.getDebutDate(), foundSubscription.get().getDebutDate());
}
@Test
@DisplayName("FindByCustomerId should return empty Optional when phone number doesn't exist")
void testFindByPhoneNumberNotFound() {
Optional<Subscription> foundSubscription = repository.findByCustomerId(UUID.fromString("0000000-0000-0000-0000-000000000000"));
assertTrue(foundSubscription.isEmpty());
}
@Test
@DisplayName("ExistsById should return true when ID exists")
void testExistsByIdExists() {
boolean exists = repository.existsById(Subscription1.getId());
assertTrue(exists);
}
@Test
@DisplayName("ExistsById should return false when ID doesn't exist")
void testExistsByIdNotExists() {
UUID nonExistentId = UUID.randomUUID();
boolean exists = repository.existsById(nonExistentId);
assertFalse(exists);
}
}
}

View File

@@ -0,0 +1,109 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.usecase;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionDTO;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionInfo;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity.Subscription;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.SubscriptionNotFoundException;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.NotValidSubscriptionException;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.repository.SubscriptionRepository;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.usecase.SubscriptionUseCase;
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.util.Optional;
import java.util.UUID;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class SubscribeUseCaseTest {
@Mock
private SubscriptionRepository subscriptionRepository;
@InjectMocks
private SubscriptionUseCase subscriptionUseCase;
private UUID subscriptionId;
private Subscription testSubscription;
private SubscriptionInfo validSubscriptionInfo;
@BeforeEach
void setUp() {
subscriptionId = UUID.randomUUID();
testSubscription = Subscription.builder()
.id(subscriptionId)
.customerId(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"))
.duration(12)
.paymentMethod("CB")
.build();
validSubscriptionInfo = new SubscriptionInfo(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"), 12, "CB");
}
@Nested
@DisplayName("Register subscription tests")
class RegisterSubscriptionTests {
@Test
@DisplayName("Should register subscription when valid data is provided")
void testRegisterSubscriptionWithValidData() throws NotValidSubscriptionException {
when(subscriptionRepository.save(any(Subscription.class))).thenReturn(testSubscription);
UUID registeredId = subscriptionUseCase.registerSubscription(validSubscriptionInfo);
assertNotNull(registeredId);
assertEquals(subscriptionId, registeredId);
verify(subscriptionRepository, times(1)).save(any(Subscription.class));
}
@Test
@DisplayName("Should throw exception when subscription data is not valid")
void testRegisterSubscriptionWithInvalidData() {
SubscriptionInfo invalidSubscriptionInfo = new SubscriptionInfo(null, null, "");
assertThrows(NotValidSubscriptionException.class,
() -> subscriptionUseCase.registerSubscription(invalidSubscriptionInfo));
verify(subscriptionRepository, never()).save(any(Subscription.class));
}
}
@Nested
@DisplayName("Find subscription tests")
class FindSubscriptionTests {
@Test
@DisplayName("Should return subscription when phone number exists")
void testFindSubscriptionByCustomerId() {
when(subscriptionRepository.findByCustomerId(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"))).thenReturn(Optional.of(testSubscription));
Optional<SubscriptionDTO> foundSubscription = subscriptionUseCase.findSubscriptionByCustomerId(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"));
assertTrue(foundSubscription.isPresent());
assertEquals(testSubscription.getId(), foundSubscription.get().getId());
assertEquals(testSubscription.getDebutDate(), foundSubscription.get().getDebutDate());
verify(subscriptionRepository, times(1)).findByCustomerId(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"));
}
@Test
@DisplayName("Should return empty Optional when phone number doesn't exist")
void testFindSubscriptionByPhoneNumberNotFound() {
when(subscriptionRepository.findByCustomerId(UUID.fromString("0000000-0000-0000-0000-000000000000"))).thenReturn(Optional.empty());
Optional<SubscriptionDTO> foundSubscription = subscriptionUseCase.findSubscriptionByCustomerId(UUID.fromString("0000000-0000-0000-0000-000000000000"));
assertTrue(foundSubscription.isEmpty());
verify(subscriptionRepository, times(1)).findByCustomerId(UUID.fromString("0000000-0000-0000-0000-000000000000"));
}
}
}

View File

@@ -0,0 +1,121 @@
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.validator;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionInfo;
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.NotValidSubscriptionException;
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.util.UUID;
import static org.junit.jupiter.api.Assertions.*;
public class SubscriptionValidatorTest {
@Test
@DisplayName("Should validate subscription with valid data")
void testValidateValidSubscription() {
SubscriptionInfo validSubscription = new SubscriptionInfo(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"), 12, "CB");
assertDoesNotThrow(() -> SubscriptionValidator.validate(validSubscription));
}
@Nested
@DisplayName("customer id validation tests")
class CustomerIdValidationTests {
@Test
@DisplayName("Should throw exception when customer id is null")
void testValidateBlankCustomerId() {
SubscriptionInfo subscriptionWithNullCustomerId = new SubscriptionInfo(null, 12, "CB");
NotValidSubscriptionException exception = assertThrows(
NotValidSubscriptionException.class,
() -> SubscriptionValidator.validate(subscriptionWithNullCustomerId)
);
assertEquals(SubscriptionValidator.CUSTOMER_ID_CANNOT_BE_NULL,
exception.getMessage());
}
/**@ParameterizedTest
@ValueSource(strings = {" ", " ", "\t", "\n"})
@DisplayName("Should throw exception when customer id contains only whitespace")
void testValidateWhitespaceCustomerId(String whitespace) {
SubscriptionInfo subscriptionWithWhitespaceFirstName = new SubscriptionInfo(whitespace, 12, "CB");
NotValidSubscriptionException exception = assertThrows(
NotValidSubscriptionException.class,
() -> SubscriptionValidator.validate(subscriptionWithWhitespaceCustomerId)
);
assertEquals(SubscriptionValidator.CUSTOMER_ID_CANNOT_BE_BLANK, exception.getMessage());
}**/
}
@Nested
@DisplayName("Duration validation tests")
class DurationValidationTests {
@Test
@DisplayName("Should throw exception when duration is blank")
void testValidateBlankDuration() {
SubscriptionInfo subscriptionWithBlankDuration = new SubscriptionInfo(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"), null, "CB");
NotValidSubscriptionException exception = assertThrows(
NotValidSubscriptionException.class,
() -> SubscriptionValidator.validate(subscriptionWithBlankDuration)
);
assertEquals(SubscriptionValidator.DURATION_CANNOT_BE_NULL, exception.getMessage());
}
/**@ParameterizedTest
@ValueSource(strings = {" ", " ", "\t", "\n"})
@DisplayName("Should throw exception when last name contains only whitespace")
void testValidateWhitespaceDuration(String whitespace) {
SubscriptionInfo subscriptionWithWhitespaceDuration = new SubscriptionInfo(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"), whitespace, "CB");
NotValidSubscriptionException exception = assertThrows(
NotValidSubscriptionException.class,
() -> SubscriptionValidator.validate(subscriptionWithWhitespaceDuration)
);
assertEquals(SubscriptionValidator.DURATION_CANNOT_BE_BLANK, exception.getMessage());
}**/
}
@Nested
@DisplayName("Payment method validation tests")
class PaymentMethodValidationTests {
@Test
@DisplayName("Should throw exception when payment method is blank")
void testValidateBlankPaymentMethod() {
SubscriptionInfo subscriptionWithBlankPaymentMethod = new SubscriptionInfo(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"), 12, "");
NotValidSubscriptionException exception = assertThrows(
NotValidSubscriptionException.class,
() -> SubscriptionValidator.validate(subscriptionWithBlankPaymentMethod)
);
assertEquals(SubscriptionValidator.PAYMENT_METHOD_CANNOT_BE_BLANK, exception.getMessage());
}
@ParameterizedTest
@ValueSource(strings = {" ", " ", "\t", "\n"})
@DisplayName("Should throw exception when payment method contains only whitespace")
void testValidateWhitespacePhoneNumber(String whitespace) {
SubscriptionInfo subscriptionWithWhitespacePaymentMethod = new SubscriptionInfo(UUID.fromString("123e4567-e89b-12d3-a456-426614174000"), 12, whitespace);
NotValidSubscriptionException exception = assertThrows(
NotValidSubscriptionException.class,
() -> SubscriptionValidator.validate(subscriptionWithWhitespacePaymentMethod)
);
assertEquals(SubscriptionValidator.PAYMENT_METHOD_CANNOT_BE_BLANK, exception.getMessage());
}
}
}

Some files were not shown because too many files have changed in this diff Show More