From 1f82178d1c4f10d1a96d332bb4fab991ae37943e Mon Sep 17 00:00:00 2001 From: Maxime Pierront <mpierront@mediametrie.fr> Date: Thu, 13 Mar 2025 16:06:48 +0100 Subject: [PATCH] :tada: --- .gitignore | 38 +++ pom.xml | 151 ++++++++++ .../dev62/mylibrary/customer/CustomerDTO.java | 15 + .../mylibrary/customer/CustomerInfo.java | 4 + .../customer/converter/CustomerConverter.java | 30 ++ .../mylibrary/customer/entity/Customer.java | 30 ++ .../exception/CustomerNotFoundException.java | 13 + .../IllegalCustomerPointException.java | 13 + .../exception/NotValidCustomerException.java | 8 + .../repository/CustomerRepository.java | 49 +++ .../customer/usecase/CustomerUseCase.java | 81 +++++ .../customer/validator/CustomerValidator.java | 44 +++ .../converter/CustomerConverterTest.java | 97 ++++++ .../customer/entity/CustomerTest.java | 136 +++++++++ .../CustomerNotFoundExceptionTest.java | 49 +++ .../IllegalCustomerPointExceptionTest.java | 69 +++++ .../NotValidCustomerExceptionTest.java | 61 ++++ .../repository/CustomerRepositoryTest.java | 227 ++++++++++++++ .../customer/usecase/CustomerUseCaseTest.java | 279 ++++++++++++++++++ .../validator/CustomerValidatorTest.java | 155 ++++++++++ .../mylibrary/features/RunCucumberTest.java | 14 + .../features/client/CustomerSteps.java | 223 ++++++++++++++ src/test/resources/features/client.feature | 60 ++++ 23 files changed, 1846 insertions(+) create mode 100644 .gitignore create mode 100644 pom.xml create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/CustomerDTO.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/CustomerInfo.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverter.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/Customer.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/CustomerNotFoundException.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/IllegalCustomerPointException.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/NotValidCustomerException.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepository.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/usecase/CustomerUseCase.java create mode 100644 src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/validator/CustomerValidator.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverterTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/CustomerTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/CustomerNotFoundExceptionTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/IllegalCustomerPointExceptionTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/NotValidCustomerExceptionTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepositoryTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/usecase/CustomerUseCaseTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/validator/CustomerValidatorTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/RunCucumberTest.java create mode 100644 src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/client/CustomerSteps.java create mode 100644 src/test/resources/features/client.feature diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..27ec78e --- /dev/null +++ b/pom.xml @@ -0,0 +1,151 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>fr.iut_fbleau.but3.dev62</groupId> + <artifactId>mylibrary</artifactId> + <version>1.0-SNAPSHOT</version> + + <properties> + <!-- Your java version--> + <maven.compiler.source>21</maven.compiler.source> + <maven.compiler.target>21</maven.compiler.target> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + + <!-- Main dependencies --> + <lombok.version>1.18.36</lombok.version> + + <!-- Test Verisons--> + <junit.version>5.11.4</junit.version> + <junit.platform.version>1.11.4</junit.platform.version> + <cucumber.version>7.21.1</cucumber.version> + <mockito.version>5.16.0</mockito.version> + + <!-- Maven build version --> + <maven.compiler.version>3.13.0</maven.compiler.version> + <maven.surefire.version>3.5.2</maven.surefire.version> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>io.cucumber</groupId> + <artifactId>cucumber-bom</artifactId> + <version>${cucumber.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + <dependency> + <groupId>org.junit</groupId> + <artifactId>junit-bom</artifactId> + <version>${junit.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <version>${lombok.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>io.cucumber</groupId> + <artifactId>cucumber-java</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>io.cucumber</groupId> + <artifactId>cucumber-junit-platform-engine</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-suite</artifactId> + <version>${junit.platform.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-engine</artifactId> + <version>${junit.platform.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-api</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <version>${junit.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.junit.platform</groupId> + <artifactId>junit-platform-launcher</artifactId> + <version>${junit.platform.version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-junit-jupiter</artifactId> + <version>${mockito.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>${maven.compiler.version}</version> + <configuration> + <encoding>${project.build.sourceEncoding}</encoding> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>${maven.surefire.version}</version> + <configuration> + <properties> + <!-- Work around. Surefire does not include enough + information to disambiguate between different + examples and scenarios. --> + <configurationParameters> + cucumber.junit-platform.naming-strategy=long + </configurationParameters> + </properties> + </configuration> + </plugin> + </plugins> + </build> +</project> \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/CustomerDTO.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/CustomerDTO.java new file mode 100644 index 0000000..96f626b --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/CustomerDTO.java @@ -0,0 +1,15 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer; + +import java.util.UUID; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class CustomerDTO { + private final UUID id; + private final String firstName; + private final String lastName; + private final String phoneNumber; + private final int loyaltyPoints; +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/CustomerInfo.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/CustomerInfo.java new file mode 100644 index 0000000..30ac1e4 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/CustomerInfo.java @@ -0,0 +1,4 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer; + +public record CustomerInfo(String firstName, String lastName, String phoneNumber) { +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverter.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverter.java new file mode 100644 index 0000000..e420020 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverter.java @@ -0,0 +1,30 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.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.entity.Customer; + +public final class CustomerConverter { + private CustomerConverter(){ + + } + + public static Customer toDomain(CustomerInfo newCustomer) { + return Customer.builder() + .firstName(newCustomer.firstName()) + .lastName(newCustomer.lastName()) + .phoneNumber(newCustomer.phoneNumber()) + .loyaltyPoints(0) + .build(); + } + + public static CustomerDTO toDTO(Customer customer) { + return CustomerDTO.builder() + .id(customer.getId()) + .firstName(customer.getFirstName()) + .lastName(customer.getLastName()) + .phoneNumber(customer.getPhoneNumber()) + .loyaltyPoints(customer.getLoyaltyPoints()) + .build(); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/Customer.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/Customer.java new file mode 100644 index 0000000..5a2fa69 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/Customer.java @@ -0,0 +1,30 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.entity; + + +import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.IllegalCustomerPointException; +import java.util.UUID; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter +public class Customer { + private UUID id; + private String firstName; + private String lastName; + private String phoneNumber; + private int loyaltyPoints; + + public void setRandomUUID() { + this.id = UUID.randomUUID(); + } + + public void addLoyaltyPoints(int loyaltyPointToAdd) { + this.loyaltyPoints += loyaltyPointToAdd; + } + + public void removeLoyaltyPoints(int loyaltyPointToRemove) throws IllegalCustomerPointException { + if (loyaltyPointToRemove > this.loyaltyPoints) throw new IllegalCustomerPointException(loyaltyPointToRemove, this.loyaltyPoints); + this.loyaltyPoints -= loyaltyPointToRemove; + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/CustomerNotFoundException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/CustomerNotFoundException.java new file mode 100644 index 0000000..722797c --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/CustomerNotFoundException.java @@ -0,0 +1,13 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.exception; + +import java.text.MessageFormat; +import java.util.UUID; + +public class CustomerNotFoundException extends Exception { + + public static final String THE_CUSTOMER_WITH_ID_DOES_NOT_EXIST_MESSAGE = "The customer with id {0} does not exist"; + + public CustomerNotFoundException(UUID uuid) { + super(MessageFormat.format(THE_CUSTOMER_WITH_ID_DOES_NOT_EXIST_MESSAGE, uuid)); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/IllegalCustomerPointException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/IllegalCustomerPointException.java new file mode 100644 index 0000000..1cca78b --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/IllegalCustomerPointException.java @@ -0,0 +1,13 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.exception; + +import java.text.MessageFormat; + +public class IllegalCustomerPointException extends Exception { + + public static final String CANNOT_REMOVE_LOYALTY_POINTS = "Cannot remove {0} points from {1} points"; + + public IllegalCustomerPointException(int needed, int actual) { + super(MessageFormat.format(CANNOT_REMOVE_LOYALTY_POINTS, needed, + actual)); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/NotValidCustomerException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/NotValidCustomerException.java new file mode 100644 index 0000000..47779f1 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/NotValidCustomerException.java @@ -0,0 +1,8 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.exception; + +public class NotValidCustomerException extends Exception { + + public NotValidCustomerException(String message) { + super(message); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepository.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepository.java new file mode 100644 index 0000000..7d79f32 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepository.java @@ -0,0 +1,49 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.repository; + +import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +public final class CustomerRepository { + private final List<Customer> customers = new ArrayList<>(); + + public List<Customer> findAll() { + return customers; + } + + public void deleteAll() { + customers.clear(); + } + + public Customer save(Customer newCustomer) { + Optional<Customer> optionalCustomerWithSameId = this.findById(newCustomer.getId()); + optionalCustomerWithSameId.ifPresentOrElse(customers::remove, newCustomer::setRandomUUID); + this.customers.add(newCustomer); + return newCustomer; + } + + public Optional<Customer> findById(UUID uuid) { + return this.customers.stream() + .filter(customer -> customer.getId().equals(uuid)) + .findFirst(); + } + + public boolean existsById(UUID uuid) { + return this.customers.stream() + .anyMatch(customer -> customer.getId().equals(uuid)); + } + + public Optional<Customer> findByPhoneNumber(String phoneNumber) { + return this.customers.stream() + .filter(customer -> customer.getPhoneNumber().equals(phoneNumber)) + .findFirst(); + } + + public void delete(Customer customer) { + this.customers.remove(customer); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/usecase/CustomerUseCase.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/usecase/CustomerUseCase.java new file mode 100644 index 0000000..3241c30 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/usecase/CustomerUseCase.java @@ -0,0 +1,81 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.usecase; + +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.customer.exception.CustomerNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.IllegalCustomerPointException; +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.validator.CustomerValidator; +import java.util.Optional; +import java.util.UUID; + +public final class CustomerUseCase { + + private final CustomerRepository customerRepository; + + public CustomerUseCase(CustomerRepository customerRepository) { + this.customerRepository = customerRepository; + } + + public UUID registerCustomer(CustomerInfo newCustomer) throws NotValidCustomerException { + CustomerValidator.validate(newCustomer); + Customer customerToRegister = CustomerConverter.toDomain(newCustomer); + Customer customerToRegistered = customerRepository.save(customerToRegister); + return customerToRegistered.getId(); + } + + public Optional<CustomerDTO> findCustomerByPhoneNumber(String phoneNumber) { + Optional<Customer> optionalCustomer = customerRepository.findByPhoneNumber(phoneNumber); + return optionalCustomer.map(CustomerConverter::toDTO); + } + + public CustomerDTO updateCustomer(UUID uuid, CustomerInfo customerInfo) + throws CustomerNotFoundException, NotValidCustomerException { + CustomerValidator.validate(customerInfo); + Customer customerByUUID = getCustomerIfDoesNotExistThrowCustomerNotFoundException( + uuid); + Customer customer = Customer.builder() + .id(uuid) + .firstName(customerInfo.firstName()) + .lastName(customerInfo.lastName()) + .phoneNumber(customerInfo.phoneNumber()) + .loyaltyPoints(customerByUUID.getLoyaltyPoints()) + .build(); + Customer updatedCustomer = customerRepository.save(customer); + return CustomerConverter.toDTO(updatedCustomer); + } + + public void deleteCustomer(UUID uuid) throws CustomerNotFoundException { + Customer customerToDelete = getCustomerIfDoesNotExistThrowCustomerNotFoundException(uuid); + this.customerRepository.delete(customerToDelete); + } + + public int addLoyaltyPoints(UUID uuid, int loyaltyPointToAdd) throws CustomerNotFoundException { + Customer customerToAddLoyaltyPoints = getCustomerIfDoesNotExistThrowCustomerNotFoundException( + uuid); + customerToAddLoyaltyPoints.addLoyaltyPoints(loyaltyPointToAdd); + customerRepository.save(customerToAddLoyaltyPoints); + return customerToAddLoyaltyPoints.getLoyaltyPoints(); + } + + public int subtractLoyaltyPoints(UUID uuid, int loyaltyPointToRemove) + throws CustomerNotFoundException, IllegalCustomerPointException { + Customer customerToSubtractLoyaltyPoints = getCustomerIfDoesNotExistThrowCustomerNotFoundException( + uuid); + customerToSubtractLoyaltyPoints.removeLoyaltyPoints(loyaltyPointToRemove); + customerRepository.save(customerToSubtractLoyaltyPoints); + return customerToSubtractLoyaltyPoints.getLoyaltyPoints(); + } + + private Customer getCustomerIfDoesNotExistThrowCustomerNotFoundException(UUID uuid) + throws CustomerNotFoundException { + Optional<Customer> optionalCustomerById = customerRepository.findById(uuid); + if (optionalCustomerById.isEmpty()) { + throw new CustomerNotFoundException(uuid); + } + return optionalCustomerById.get(); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/validator/CustomerValidator.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/validator/CustomerValidator.java new file mode 100644 index 0000000..d9bd9c1 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/validator/CustomerValidator.java @@ -0,0 +1,44 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.validator; + +import fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerInfo; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.NotValidCustomerException; + +public final class CustomerValidator { + + public static final String PHONE_NUMBER_IS_NOT_VALID = "Phone number is not valid"; + public static final String LAST_NAME_CANNOT_BE_BLANK = "Last name cannot be blank"; + public static final String FIRST_NAME_CANNOT_BE_BLANK = "First name cannot be blank"; + public static final String PHONE_NUMBER_REGEX = "0([67])\\d{8}"; + + private CustomerValidator() { + + } + + public static void validate(CustomerInfo newCustomer) throws NotValidCustomerException { + validateFirstName(newCustomer); + validateLastName(newCustomer); + validatePhoneNumber(newCustomer); + } + + private static void validatePhoneNumber(CustomerInfo newCustomer) + throws NotValidCustomerException { + if (newCustomer.phoneNumber().isBlank()) { + throw new NotValidCustomerException("Phone number cannot be blank"); + } + if (!newCustomer.phoneNumber().matches(PHONE_NUMBER_REGEX)) { + throw new NotValidCustomerException(PHONE_NUMBER_IS_NOT_VALID); + } + } + + private static void validateLastName(CustomerInfo newCustomer) throws NotValidCustomerException { + if (newCustomer.lastName().isBlank()) { + throw new NotValidCustomerException(LAST_NAME_CANNOT_BE_BLANK); + } + } + + private static void validateFirstName(CustomerInfo newCustomer) throws NotValidCustomerException { + if (newCustomer.firstName().isBlank()) { + throw new NotValidCustomerException(FIRST_NAME_CANNOT_BE_BLANK); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverterTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverterTest.java new file mode 100644 index 0000000..231fbab --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/converter/CustomerConverterTest.java @@ -0,0 +1,97 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.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.entity.Customer; +import java.util.UUID; +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("CustomerConverter Unit Tests") +class CustomerConverterTest { + + @Nested + @DisplayName("toDomain() method tests") + class ToDomainTests { + + @Test + @DisplayName("Should convert CustomerInfo to Customer domain object with loyalty points initialized to 0") + void shouldConvertCustomerInfoToDomain() { + // Given + CustomerInfo customerInfo = new CustomerInfo("John", "Doe", "0123456789"); + + // When + Customer result = CustomerConverter.toDomain(customerInfo); + + // Then + assertNotNull(result); + assertEquals(customerInfo.firstName(), result.getFirstName()); + assertEquals(customerInfo.lastName(), result.getLastName()); + assertEquals(customerInfo.phoneNumber(), result.getPhoneNumber()); + assertEquals(0, result.getLoyaltyPoints()); + } + } + + @Nested + @DisplayName("toDTO() method tests") + class ToDTOTests { + + @Test + @DisplayName("Should convert Customer domain object to CustomerDTO with all fields mapped correctly") + void shouldConvertCustomerToDTO() { + Customer customer = Customer.builder() + .id(UUID.randomUUID()) + .firstName("Jane") + .lastName("Smith") + .phoneNumber("9876543210") + .loyaltyPoints(100) + .build(); + + CustomerDTO result = CustomerConverter.toDTO(customer); + + assertNotNull(result); + assertEquals(customer.getId(), result.getId()); + assertEquals(customer.getFirstName(), result.getFirstName()); + assertEquals(customer.getLastName(), result.getLastName()); + assertEquals(customer.getPhoneNumber(), result.getPhoneNumber()); + assertEquals(customer.getLoyaltyPoints(), result.getLoyaltyPoints()); + } + } + + @Test + @DisplayName("Should handle null values properly when converting between objects") + void shouldHandleNullValuesGracefully() { + Customer customer = Customer.builder() + .id(UUID.randomUUID()) + .firstName(null) + .lastName("NullTest") + .phoneNumber(null) + .loyaltyPoints(50) + .build(); + + CustomerDTO result = CustomerConverter.toDTO(customer); + + assertNotNull(result); + assertNull(result.getFirstName()); + assertEquals("NullTest", result.getLastName()); + assertNull(result.getPhoneNumber()); + } + + @Test + @DisplayName("Should preserve empty string values during conversion") + void shouldPreserveEmptyStrings() { + CustomerInfo customerInfo = new CustomerInfo("", "", ""); + + Customer domainResult = CustomerConverter.toDomain(customerInfo); + CustomerDTO dtoResult = CustomerConverter.toDTO(domainResult); + + assertEquals("", dtoResult.getFirstName()); + assertEquals("", dtoResult.getLastName()); + assertEquals("", dtoResult.getPhoneNumber()); + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/CustomerTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/CustomerTest.java new file mode 100644 index 0000000..515187e --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/entity/CustomerTest.java @@ -0,0 +1,136 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.entity; + +import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.IllegalCustomerPointException; +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; + +class CustomerTest { + + @Test + @DisplayName("Builder should create a valid Customer instance") + void testCustomerBuilder() { + UUID id = UUID.randomUUID(); + String firstName = "John"; + String lastName = "Doe"; + String phoneNumber = "0123456789"; + int loyaltyPoints = 100; + + Customer customer = Customer.builder() + .id(id) + .firstName(firstName) + .lastName(lastName) + .phoneNumber(phoneNumber) + .loyaltyPoints(loyaltyPoints) + .build(); + + assertEquals(id, customer.getId()); + assertEquals(firstName, customer.getFirstName()); + assertEquals(lastName, customer.getLastName()); + assertEquals(phoneNumber, customer.getPhoneNumber()); + assertEquals(loyaltyPoints, customer.getLoyaltyPoints()); + } + + @Test + @DisplayName("setRandomUUID should change the ID to a new random UUID") + void testSetRandomUUID() { + Customer customer = Customer.builder().build(); + UUID originalId = customer.getId(); + + customer.setRandomUUID(); + + assertNotNull(customer.getId()); + assertNotEquals(originalId, customer.getId()); + } + + @Nested + @DisplayName("Loyalty Points Tests") + class LoyaltyPointsTests { + + @Test + @DisplayName("addLoyaltyPoints should correctly increment loyalty points") + void testAddLoyaltyPoints() { + Customer customer = Customer.builder() + .loyaltyPoints(50) + .build(); + int pointsToAdd = 25; + int expectedPoints = 75; + + customer.addLoyaltyPoints(pointsToAdd); + + assertEquals(expectedPoints, customer.getLoyaltyPoints()); + } + + @Test + @DisplayName("addLoyaltyPoints should handle zero points correctly") + void testAddZeroLoyaltyPoints() { + Customer customer = Customer.builder() + .loyaltyPoints(50) + .build(); + + customer.addLoyaltyPoints(0); + + assertEquals(50, customer.getLoyaltyPoints()); + } + + @Test + @DisplayName("removeLoyaltyPoints should correctly decrement loyalty points") + void testRemoveLoyaltyPoints() throws IllegalCustomerPointException { + Customer customer = Customer.builder() + .loyaltyPoints(50) + .build(); + int pointsToRemove = 20; + int expectedPoints = 30; + + customer.removeLoyaltyPoints(pointsToRemove); + + assertEquals(expectedPoints, customer.getLoyaltyPoints()); + } + + @Test + @DisplayName("removeLoyaltyPoints should handle removing exactly all points") + void testRemoveAllLoyaltyPoints() throws IllegalCustomerPointException { + Customer customer = Customer.builder() + .loyaltyPoints(50) + .build(); + + customer.removeLoyaltyPoints(50); + + assertEquals(0, customer.getLoyaltyPoints()); + } + + @Test + @DisplayName("removeLoyaltyPoints should throw exception when trying to remove more points than available") + void testRemoveTooManyLoyaltyPoints() { + Customer customer = Customer.builder() + .loyaltyPoints(50) + .build(); + int pointsToRemove = 75; + + IllegalCustomerPointException exception = assertThrows( + IllegalCustomerPointException.class, + () -> customer.removeLoyaltyPoints(pointsToRemove) + ); + + assertEquals(50, customer.getLoyaltyPoints()); + + assertTrue(exception.getMessage().contains(String.valueOf(pointsToRemove))); + assertTrue(exception.getMessage().contains(String.valueOf(customer.getLoyaltyPoints()))); + } + + @Test + @DisplayName("removeLoyaltyPoints should handle removing zero points correctly") + void testRemoveZeroLoyaltyPoints() throws IllegalCustomerPointException { + Customer customer = Customer.builder() + .loyaltyPoints(50) + .build(); + + customer.removeLoyaltyPoints(0); + + assertEquals(50, customer.getLoyaltyPoints()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/CustomerNotFoundExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/CustomerNotFoundExceptionTest.java new file mode 100644 index 0000000..fcde9da --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/CustomerNotFoundExceptionTest.java @@ -0,0 +1,49 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.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 CustomerNotFoundExceptionTest { + + @Test + @DisplayName("Exception message should contain the UUID provided") + void testExceptionMessageContainsUUID() { + UUID uuid = UUID.randomUUID(); + + CustomerNotFoundException exception = new CustomerNotFoundException(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(); + + CustomerNotFoundException exception = new CustomerNotFoundException(uuid); + + String expectedFormatWithPlaceholder = "The customer with id {0} does not exist"; + assertEquals(CustomerNotFoundException.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 CustomerNotFoundException(uuid); + } catch (CustomerNotFoundException e) { + String expectedMessage = String.format("The customer with id %s does not exist", uuid); + assertEquals(expectedMessage, e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/IllegalCustomerPointExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/IllegalCustomerPointExceptionTest.java new file mode 100644 index 0000000..c89306c --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/IllegalCustomerPointExceptionTest.java @@ -0,0 +1,69 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.exception; + +import java.text.MessageFormat; +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 IllegalCustomerPointExceptionTest { + + @Test + @DisplayName("Exception message should contain the needed and actual points") + void testExceptionMessageContainsPoints() { + int neededPoints = 100; + int actualPoints = 50; + + IllegalCustomerPointException exception = new IllegalCustomerPointException(neededPoints, actualPoints); + + String expectedMessage = "Cannot remove 100 points from 50 points"; + assertEquals(expectedMessage, exception.getMessage()); + } + + @ParameterizedTest + @CsvSource({ + "100, 50", + "75, 25", + "200, 150", + "1000, 750" + }) + @DisplayName("Exception message should be formatted correctly for different point values") + void testExceptionMessageForDifferentPointValues(int neededPoints, int actualPoints) { + IllegalCustomerPointException exception = new IllegalCustomerPointException(neededPoints, actualPoints); + + String expectedMessage = MessageFormat.format(IllegalCustomerPointException.CANNOT_REMOVE_LOYALTY_POINTS, neededPoints, actualPoints); + assertEquals(expectedMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should use the correct constant message format") + void testExceptionUsesConstantMessageFormat() { + int neededPoints = 100; + int actualPoints = 50; + + IllegalCustomerPointException exception = new IllegalCustomerPointException(neededPoints, actualPoints); + + String expectedFormatWithPlaceholder = "Cannot remove {0} points from {1} points"; + assertEquals(IllegalCustomerPointException.CANNOT_REMOVE_LOYALTY_POINTS, + expectedFormatWithPlaceholder); + assertTrue(exception.getMessage().contains(String.valueOf(neededPoints))); + assertTrue(exception.getMessage().contains(String.valueOf(actualPoints))); + } + + @Test + @DisplayName("Exception should be properly thrown and caught") + void testExceptionCanBeThrownAndCaught() { + int neededPoints = 100; + int actualPoints = 50; + + try { + throw new IllegalCustomerPointException(neededPoints, actualPoints); + } catch (IllegalCustomerPointException e) { + String expectedMessage = String.format("Cannot remove %d points from %d points", neededPoints, actualPoints); + assertEquals(expectedMessage, e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/NotValidCustomerExceptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/NotValidCustomerExceptionTest.java new file mode 100644 index 0000000..3fba6e1 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/exception/NotValidCustomerExceptionTest.java @@ -0,0 +1,61 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.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 NotValidCustomerExceptionTest { + + @Test + @DisplayName("Exception should be created with the provided message") + void testExceptionCreation() { + String errorMessage = "Customer data is not valid"; + + NotValidCustomerException exception = new NotValidCustomerException(errorMessage); + + assertEquals(errorMessage, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = { + "First name is required", + "Last name cannot be empty", + "Phone number format is invalid", + "Customer age must be above 18" + }) + @DisplayName("Exception should handle different validation messages") + void testExceptionWithDifferentMessages(String errorMessage) { + NotValidCustomerException exception = new NotValidCustomerException(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(NotValidCustomerException.class, () -> { + throw new NotValidCustomerException(errorMessage); + }); + + assertEquals(errorMessage, exception.getMessage()); + } + + @Test + @DisplayName("Exception should be catchable as a general Exception") + void testExceptionInheritance() { + String errorMessage = "Invalid customer data"; + + try { + throw new NotValidCustomerException(errorMessage); + } catch (Exception e) { + assertEquals(NotValidCustomerException.class, e.getClass()); + assertEquals(errorMessage, e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepositoryTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepositoryTest.java new file mode 100644 index 0000000..4495493 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/repository/CustomerRepositoryTest.java @@ -0,0 +1,227 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.repository; + +import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer; +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.*; + +class CustomerRepositoryTest { + + private CustomerRepository repository; + private Customer customer1; + private Customer customer2; + + @BeforeEach + void setUp() { + repository = new CustomerRepository(); + + customer1 = Customer.builder() + .firstName("John") + .lastName("Doe") + .phoneNumber("0123456789") + .loyaltyPoints(100) + .build(); + customer1.setRandomUUID(); + + customer2 = Customer.builder() + .firstName("Jane") + .lastName("Smith") + .phoneNumber("9876543210") + .loyaltyPoints(200) + .build(); + customer2.setRandomUUID(); + } + + @Test + @DisplayName("New repository should be empty") + void testNewRepositoryIsEmpty() { + List<Customer> customers = repository.findAll(); + + assertTrue(customers.isEmpty()); + assertEquals(0, customers.size()); + } + + @Nested + @DisplayName("Save operations") + class SaveOperations { + + @Test + @DisplayName("Save should add a new customer") + void testSaveNewCustomer() { + Customer savedCustomer = repository.save(customer1); + + assertEquals(1, repository.findAll().size()); + assertEquals(customer1.getId(), savedCustomer.getId()); + assertEquals(customer1.getFirstName(), savedCustomer.getFirstName()); + } + + @Test + @DisplayName("Save should update existing customer with same ID") + void testSaveUpdatesExistingCustomer() { + repository.save(customer1); + + UUID id = customer1.getId(); + Customer updatedCustomer = Customer.builder() + .id(id) + .firstName("John") + .lastName("Updated") + .phoneNumber("1111111111") + .loyaltyPoints(150) + .build(); + + Customer savedCustomer = repository.save(updatedCustomer); + + assertEquals(1, repository.findAll().size()); + assertEquals(id, savedCustomer.getId()); + assertEquals("Updated", savedCustomer.getLastName()); + assertEquals("1111111111", savedCustomer.getPhoneNumber()); + assertEquals(150, savedCustomer.getLoyaltyPoints()); + } + + @Test + @DisplayName("Save multiple customers should add all of them") + void testSaveMultipleCustomers() { + repository.save(customer1); + repository.save(customer2); + + List<Customer> customers = repository.findAll(); + + assertEquals(2, customers.size()); + assertTrue(customers.contains(customer1)); + assertTrue(customers.contains(customer2)); + } + } + + @Nested + @DisplayName("Find operations") + class FindOperations { + + @BeforeEach + void setUpCustomers() { + repository.save(customer1); + repository.save(customer2); + } + + @Test + @DisplayName("FindAll should return all customers") + void testFindAll() { + List<Customer> customers = repository.findAll(); + + assertEquals(2, customers.size()); + assertTrue(customers.contains(customer1)); + assertTrue(customers.contains(customer2)); + } + + @Test + @DisplayName("FindById should return customer with matching ID") + void testFindById() { + Optional<Customer> foundCustomer = repository.findById(customer1.getId()); + + assertTrue(foundCustomer.isPresent()); + assertEquals(customer1.getFirstName(), foundCustomer.get().getFirstName()); + assertEquals(customer1.getLastName(), foundCustomer.get().getLastName()); + } + + @Test + @DisplayName("FindById should return empty Optional when ID doesn't exist") + void testFindByIdNotFound() { + UUID nonExistentId = UUID.randomUUID(); + + Optional<Customer> foundCustomer = repository.findById(nonExistentId); + + assertTrue(foundCustomer.isEmpty()); + } + + @Test + @DisplayName("FindByPhoneNumber should return customer with matching phone number") + void testFindByPhoneNumber() { + Optional<Customer> foundCustomer = repository.findByPhoneNumber("0123456789"); + + assertTrue(foundCustomer.isPresent()); + assertEquals(customer1.getId(), foundCustomer.get().getId()); + assertEquals(customer1.getFirstName(), foundCustomer.get().getFirstName()); + } + + @Test + @DisplayName("FindByPhoneNumber should return empty Optional when phone number doesn't exist") + void testFindByPhoneNumberNotFound() { + Optional<Customer> foundCustomer = repository.findByPhoneNumber("0000000000"); + + assertTrue(foundCustomer.isEmpty()); + } + + @Test + @DisplayName("ExistsById should return true when ID exists") + void testExistsByIdExists() { + boolean exists = repository.existsById(customer1.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); + } + } + + @Nested + @DisplayName("Delete operations") + class DeleteOperations { + + @BeforeEach + void setUpCustomers() { + repository.save(customer1); + repository.save(customer2); + } + + @Test + @DisplayName("Delete should remove the specified customer") + void testDelete() { + repository.delete(customer1); + + List<Customer> customers = repository.findAll(); + + assertEquals(1, customers.size()); + assertFalse(customers.contains(customer1)); + assertTrue(customers.contains(customer2)); + } + + @Test + @DisplayName("DeleteAll should remove all customers") + void testDeleteAll() { + repository.deleteAll(); + + List<Customer> customers = repository.findAll(); + + assertTrue(customers.isEmpty()); + assertEquals(0, customers.size()); + } + + @Test + @DisplayName("Delete should not throw exception when customer doesn't exist") + void testDeleteNonExistentCustomer() { + Customer nonExistentCustomer = Customer.builder() + .firstName("Non") + .lastName("Existent") + .phoneNumber("0000000000") + .build(); + nonExistentCustomer.setRandomUUID(); + + assertDoesNotThrow(() -> repository.delete(nonExistentCustomer)); + + assertEquals(2, repository.findAll().size()); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/usecase/CustomerUseCaseTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/usecase/CustomerUseCaseTest.java new file mode 100644 index 0000000..7159bdb --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/usecase/CustomerUseCaseTest.java @@ -0,0 +1,279 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.usecase; + +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.CustomerNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.IllegalCustomerPointException; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.NotValidCustomerException; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository; +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) +class CustomerUseCaseTest { + + @Mock + private CustomerRepository customerRepository; + + @InjectMocks + private CustomerUseCase customerUseCase; + + private UUID customerId; + private Customer testCustomer; + private CustomerInfo validCustomerInfo; + + @BeforeEach + void setUp() { + customerId = UUID.randomUUID(); + testCustomer = Customer.builder() + .id(customerId) + .firstName("John") + .lastName("Doe") + .phoneNumber("0612345678") + .loyaltyPoints(100) + .build(); + + validCustomerInfo = new CustomerInfo("John", "Doe", "0612345678"); + } + + @Nested + @DisplayName("Register customer tests") + class RegisterCustomerTests { + + @Test + @DisplayName("Should register customer when valid data is provided") + void testRegisterCustomerWithValidData() throws NotValidCustomerException { + when(customerRepository.save(any(Customer.class))).thenReturn(testCustomer); + + UUID registeredId = customerUseCase.registerCustomer(validCustomerInfo); + + assertNotNull(registeredId); + assertEquals(customerId, registeredId); + verify(customerRepository, times(1)).save(any(Customer.class)); + } + + @Test + @DisplayName("Should throw exception when customer data is not valid") + void testRegisterCustomerWithInvalidData() { + CustomerInfo invalidCustomerInfo = new CustomerInfo("", "", ""); + + assertThrows(NotValidCustomerException.class, + () -> customerUseCase.registerCustomer(invalidCustomerInfo)); + + verify(customerRepository, never()).save(any(Customer.class)); + } + } + + @Nested + @DisplayName("Find customer tests") + class FindCustomerTests { + + @Test + @DisplayName("Should return customer when phone number exists") + void testFindCustomerByPhoneNumber() { + when(customerRepository.findByPhoneNumber("0612345678")).thenReturn(Optional.of(testCustomer)); + + Optional<CustomerDTO> foundCustomer = customerUseCase.findCustomerByPhoneNumber("0612345678"); + + assertTrue(foundCustomer.isPresent()); + assertEquals(testCustomer.getId(), foundCustomer.get().getId()); + assertEquals(testCustomer.getFirstName(), foundCustomer.get().getFirstName()); + verify(customerRepository, times(1)).findByPhoneNumber("0612345678"); + } + + @Test + @DisplayName("Should return empty Optional when phone number doesn't exist") + void testFindCustomerByPhoneNumberNotFound() { + when(customerRepository.findByPhoneNumber("0799999999")).thenReturn(Optional.empty()); + + Optional<CustomerDTO> foundCustomer = customerUseCase.findCustomerByPhoneNumber("0799999999"); + + assertTrue(foundCustomer.isEmpty()); + verify(customerRepository, times(1)).findByPhoneNumber("0799999999"); + } + } + + @Nested + @DisplayName("Update customer tests") + class UpdateCustomerTests { + + @Test + @DisplayName("Should update customer when valid data is provided") + void testUpdateCustomerWithValidData() throws CustomerNotFoundException, NotValidCustomerException { + when(customerRepository.findById(customerId)).thenReturn(Optional.of(testCustomer)); + + Customer updatedCustomer = Customer.builder() + .id(customerId) + .firstName("John") + .lastName("Updated") + .phoneNumber("0712345678") + .loyaltyPoints(100) + .build(); + + when(customerRepository.save(any(Customer.class))).thenReturn(updatedCustomer); + + CustomerInfo updateInfo = new CustomerInfo("John", "Updated", "0712345678"); + + CustomerDTO result = customerUseCase.updateCustomer(customerId, updateInfo); + + assertNotNull(result); + assertEquals(customerId, result.getId()); + assertEquals("Updated", result.getLastName()); + assertEquals("0712345678", result.getPhoneNumber()); + verify(customerRepository, times(1)).findById(customerId); + verify(customerRepository, times(1)).save(any(Customer.class)); + } + + @Test + @DisplayName("Should throw exception when customer ID doesn't exist") + void testUpdateCustomerNotFound() { + UUID nonExistentId = UUID.randomUUID(); + when(customerRepository.findById(nonExistentId)).thenReturn(Optional.empty()); + + CustomerInfo updateInfo = new CustomerInfo("John", "Updated", "0712345678"); + + assertThrows(CustomerNotFoundException.class, + () -> customerUseCase.updateCustomer(nonExistentId, updateInfo)); + + verify(customerRepository, times(1)).findById(nonExistentId); + verify(customerRepository, never()).save(any(Customer.class)); + } + + @Test + @DisplayName("Should throw exception when update data is not valid") + void testUpdateCustomerWithInvalidData() { + CustomerInfo invalidUpdateInfo = new CustomerInfo("", "", ""); + + assertThrows(NotValidCustomerException.class, + () -> customerUseCase.updateCustomer(customerId, invalidUpdateInfo)); + + verify(customerRepository, never()).findById(any(UUID.class)); + verify(customerRepository, never()).save(any(Customer.class)); + } + } + + @Nested + @DisplayName("Delete customer tests") + class DeleteCustomerTests { + + @Test + @DisplayName("Should delete customer when ID exists") + void testDeleteCustomer() throws CustomerNotFoundException { + when(customerRepository.findById(customerId)).thenReturn(Optional.of(testCustomer)); + doNothing().when(customerRepository).delete(testCustomer); + + customerUseCase.deleteCustomer(customerId); + + verify(customerRepository, times(1)).findById(customerId); + verify(customerRepository, times(1)).delete(testCustomer); + } + + @Test + @DisplayName("Should throw exception when customer ID doesn't exist") + void testDeleteCustomerNotFound() { + UUID nonExistentId = UUID.randomUUID(); + when(customerRepository.findById(nonExistentId)).thenReturn(Optional.empty()); + + assertThrows(CustomerNotFoundException.class, + () -> customerUseCase.deleteCustomer(nonExistentId)); + + verify(customerRepository, times(1)).findById(nonExistentId); + verify(customerRepository, never()).delete(any(Customer.class)); + } + } + + @Nested + @DisplayName("Loyalty points tests") + class LoyaltyPointsTests { + + @Test + @DisplayName("Should add loyalty points to customer") + void testAddLoyaltyPoints() throws CustomerNotFoundException { + when(customerRepository.findById(customerId)).thenReturn(Optional.of(testCustomer)); + when(customerRepository.save(testCustomer)).thenReturn(testCustomer); + + int initialPoints = testCustomer.getLoyaltyPoints(); + int pointsToAdd = 50; + int expectedPoints = initialPoints + pointsToAdd; + + int newPoints = customerUseCase.addLoyaltyPoints(customerId, pointsToAdd); + + assertEquals(expectedPoints, newPoints); + assertEquals(expectedPoints, testCustomer.getLoyaltyPoints()); + verify(customerRepository, times(1)).findById(customerId); + verify(customerRepository, times(1)).save(testCustomer); + } + + @Test + @DisplayName("Should throw exception when adding points to non-existent customer") + void testAddLoyaltyPointsToNonExistentCustomer() { + UUID nonExistentId = UUID.randomUUID(); + when(customerRepository.findById(nonExistentId)).thenReturn(Optional.empty()); + + assertThrows(CustomerNotFoundException.class, + () -> customerUseCase.addLoyaltyPoints(nonExistentId, 50)); + + verify(customerRepository, times(1)).findById(nonExistentId); + verify(customerRepository, never()).save(any(Customer.class)); + } + + @Test + @DisplayName("Should subtract loyalty points from customer") + void testSubtractLoyaltyPoints() throws CustomerNotFoundException, IllegalCustomerPointException { + when(customerRepository.findById(customerId)).thenReturn(Optional.of(testCustomer)); + when(customerRepository.save(testCustomer)).thenReturn(testCustomer); + + int initialPoints = testCustomer.getLoyaltyPoints(); + int pointsToRemove = 30; + int expectedPoints = initialPoints - pointsToRemove; + + int newPoints = customerUseCase.subtractLoyaltyPoints(customerId, pointsToRemove); + + assertEquals(expectedPoints, newPoints); + assertEquals(expectedPoints, testCustomer.getLoyaltyPoints()); + verify(customerRepository, times(1)).findById(customerId); + verify(customerRepository, times(1)).save(testCustomer); + } + + @Test + @DisplayName("Should throw exception when trying to remove more points than available") + void testSubtractTooManyLoyaltyPoints() { + when(customerRepository.findById(customerId)).thenReturn(Optional.of(testCustomer)); + + int pointsToRemove = 200; + + assertThrows(IllegalCustomerPointException.class, + () -> customerUseCase.subtractLoyaltyPoints(customerId, pointsToRemove)); + + verify(customerRepository, times(1)).findById(customerId); + verify(customerRepository, never()).save(any(Customer.class)); + } + + @Test + @DisplayName("Should throw exception when subtracting points from non-existent customer") + void testSubtractLoyaltyPointsFromNonExistentCustomer() { + UUID nonExistentId = UUID.randomUUID(); + when(customerRepository.findById(nonExistentId)).thenReturn(Optional.empty()); + + assertThrows(CustomerNotFoundException.class, + () -> customerUseCase.subtractLoyaltyPoints(nonExistentId, 50)); + + verify(customerRepository, times(1)).findById(nonExistentId); + verify(customerRepository, never()).save(any(Customer.class)); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/validator/CustomerValidatorTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/validator/CustomerValidatorTest.java new file mode 100644 index 0000000..29bb954 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/customer/validator/CustomerValidatorTest.java @@ -0,0 +1,155 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.customer.validator; + +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 static org.junit.jupiter.api.Assertions.*; + +class CustomerValidatorTest { + + @Test + @DisplayName("Should validate customer with valid data") + void testValidateValidCustomer() { + CustomerInfo validCustomer = new CustomerInfo("John", "Doe", "0612345678"); + + assertDoesNotThrow(() -> CustomerValidator.validate(validCustomer)); + } + + @Nested + @DisplayName("First name validation tests") + class FirstNameValidationTests { + + @Test + @DisplayName("Should throw exception when first name is blank") + void testValidateBlankFirstName() { + CustomerInfo customerWithBlankFirstName = new CustomerInfo("", "Doe", "0612345678"); + + NotValidCustomerException exception = assertThrows( + NotValidCustomerException.class, + () -> CustomerValidator.validate(customerWithBlankFirstName) + ); + + assertEquals(CustomerValidator.FIRST_NAME_CANNOT_BE_BLANK, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {" ", " ", "\t", "\n"}) + @DisplayName("Should throw exception when first name contains only whitespace") + void testValidateWhitespaceFirstName(String whitespace) { + CustomerInfo customerWithWhitespaceFirstName = new CustomerInfo(whitespace, "Doe", "0612345678"); + + NotValidCustomerException exception = assertThrows( + NotValidCustomerException.class, + () -> CustomerValidator.validate(customerWithWhitespaceFirstName) + ); + + assertEquals(CustomerValidator.FIRST_NAME_CANNOT_BE_BLANK, exception.getMessage()); + } + } + + @Nested + @DisplayName("Last name validation tests") + class LastNameValidationTests { + + @Test + @DisplayName("Should throw exception when last name is blank") + void testValidateBlankLastName() { + CustomerInfo customerWithBlankLastName = new CustomerInfo("John", "", "0612345678"); + + NotValidCustomerException exception = assertThrows( + NotValidCustomerException.class, + () -> CustomerValidator.validate(customerWithBlankLastName) + ); + + assertEquals(CustomerValidator.LAST_NAME_CANNOT_BE_BLANK, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {" ", " ", "\t", "\n"}) + @DisplayName("Should throw exception when last name contains only whitespace") + void testValidateWhitespaceLastName(String whitespace) { + CustomerInfo customerWithWhitespaceLastName = new CustomerInfo("John", whitespace, "0612345678"); + + NotValidCustomerException exception = assertThrows( + NotValidCustomerException.class, + () -> CustomerValidator.validate(customerWithWhitespaceLastName) + ); + + assertEquals(CustomerValidator.LAST_NAME_CANNOT_BE_BLANK, exception.getMessage()); + } + } + + @Nested + @DisplayName("Phone number validation tests") + class PhoneNumberValidationTests { + + @Test + @DisplayName("Should throw exception when phone number is blank") + void testValidateBlankPhoneNumber() { + CustomerInfo customerWithBlankPhoneNumber = new CustomerInfo("John", "Doe", ""); + + NotValidCustomerException exception = assertThrows( + NotValidCustomerException.class, + () -> CustomerValidator.validate(customerWithBlankPhoneNumber) + ); + + assertEquals("Phone number cannot be blank", exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = {" ", " ", "\t", "\n"}) + @DisplayName("Should throw exception when phone number contains only whitespace") + void testValidateWhitespacePhoneNumber(String whitespace) { + CustomerInfo customerWithWhitespacePhoneNumber = new CustomerInfo("John", "Doe", whitespace); + + NotValidCustomerException exception = assertThrows( + NotValidCustomerException.class, + () -> CustomerValidator.validate(customerWithWhitespacePhoneNumber) + ); + + assertEquals("Phone number cannot be blank", exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = { + "0512345678", // Invalid prefix (not 06 or 07) + "0812345678", // Invalid prefix (not 06 or 07) + "061234567", // Too short (missing one digit) + "06123456789", // Too long (one extra digit) + "6123456789", // Missing leading 0 + "O612345678", // Letter O instead of zero + "+33612345678", // International format not supported + "06 12 34 56 78" // Contains spaces + }) + @DisplayName("Should throw exception when phone number format is invalid") + void testValidateInvalidPhoneNumberFormat(String invalidPhoneNumber) { + CustomerInfo customerWithInvalidPhoneNumber = new CustomerInfo("John", "Doe", invalidPhoneNumber); + + NotValidCustomerException exception = assertThrows( + NotValidCustomerException.class, + () -> CustomerValidator.validate(customerWithInvalidPhoneNumber) + ); + + assertEquals(CustomerValidator.PHONE_NUMBER_IS_NOT_VALID, exception.getMessage()); + } + + @ParameterizedTest + @ValueSource(strings = { + "0612345678", // Valid 06 number + "0712345678", // Valid 07 number + "0699999999", // Valid 06 number with all 9s + "0700000000" // Valid 07 number with all 0s + }) + @DisplayName("Should validate when phone number format is valid") + void testValidateValidPhoneNumberFormat(String validPhoneNumber) { + CustomerInfo customerWithValidPhoneNumber = new CustomerInfo("John", "Doe", validPhoneNumber); + + assertDoesNotThrow(() -> CustomerValidator.validate(customerWithValidPhoneNumber)); + } + } +} \ No newline at end of file diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/RunCucumberTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/RunCucumberTest.java new file mode 100644 index 0000000..6080357 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/RunCucumberTest.java @@ -0,0 +1,14 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.features; + +import org.junit.platform.suite.api.*; + +import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME; +import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME; + +@Suite +@IncludeEngines("cucumber") +@SelectClasspathResource("features") +@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty") +@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "fr.iut_fbleau.but3.dev62.mylibrary.features") +public class RunCucumberTest { +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/client/CustomerSteps.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/client/CustomerSteps.java new file mode 100644 index 0000000..e71f786 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/client/CustomerSteps.java @@ -0,0 +1,223 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.features.client; + +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.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.CustomerNotFoundException; +import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.IllegalCustomerPointException; +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.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.Optional; +import java.util.UUID; + +public class CustomerSteps { + + private final CustomerRepository customerRepository = new CustomerRepository(); + private final CustomerUseCase customerUseCase = new CustomerUseCase(customerRepository); + + private final Map<String, UUID> customerPhoneUUID = new HashMap<>(); + private UUID customerRegistration; + private Optional<CustomerDTO> customerByPhoneNumber; + private Customer updatedCustomer; + private IllegalCustomerPointException illegalCustomerPointException; + private NotValidCustomerException notValidCustomerException; + + @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 customer with the following information:") + public void iCreateANewCustomerWithTheFollowingInformation(DataTable dataTable) throws NotValidCustomerException { + List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class); + + Map<String, String> customerInfo = rows.getFirst(); + + CustomerInfo newCustomer = new CustomerInfo( + customerInfo.get("prenom"), + customerInfo.get("nom"), + customerInfo.get("numeroTelephone") + ); + + customerRegistration = customerUseCase.registerCustomer(newCustomer); + } + + @And("the system now has {int} customers") + public void theSystemNowHasCustomers(int numberOfCustomers) { + assertEquals(numberOfCustomers, customerRepository.findAll().size()); + } + + @And("the customer has {int} loyalty points") + public void theCustomerHasLoyaltyPoints(int loyaltyPoints) { + Customer customer = customerRepository.findById(customerRegistration).orElseThrow(); + assertEquals(loyaltyPoints, customer.getLoyaltyPoints()); + } + + @Then("a new customer is created") + public void aNewCustomerIsCreated() { + assertNotNull(customerRegistration); + } + + @When("I request the customer with phone number {string}") + public void iRequestTheCustomerWithPhoneNumber(String phoneNumber) { + customerByPhoneNumber = customerUseCase.findCustomerByPhoneNumber(phoneNumber); + } + + @Then("I receive the following customer information:") + public void iReceiveTheFollowingCustomerInformation(DataTable dataTable) { + List<Map<String, String>> customers = dataTable.asMaps(String.class, String.class); + Map<String, String> customerInfo = customers.getFirst(); + assertTrue(customerByPhoneNumber.isPresent()); + CustomerDTO customerDTO = customerByPhoneNumber.get(); + assertEquals(customerInfo.get("prenom"), customerDTO.getFirstName()); + assertEquals(customerInfo.get("nom"), customerDTO.getLastName()); + assertEquals(customerInfo.get("numeroTelephone"), customerDTO.getPhoneNumber()); + } + + @When("I update customer {string} with the following information:") + public void iUpdateCustomerWithTheFollowingInformation(String phoneNumber, DataTable dataTable) + throws CustomerNotFoundException, NotValidCustomerException { + List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class); + + Map<String, String> customerData = rows.getFirst(); + CustomerInfo customerInfo = new CustomerInfo( + customerData.get("prenom"), + customerData.get("nom"), + customerData.get("numeroTelephone") + ); + UUID uuid = customerPhoneUUID.get(phoneNumber); + customerUseCase.updateCustomer(uuid, customerInfo); + } + + @Then("the customer {string} has the following updated information:") + public void theCustomerHasTheFollowingUpdatedInformation(String phoneNumber, DataTable dataTable) { + List<Map<String, String>> rows = dataTable.asMaps(String.class, String.class); + + Map<String, String> updatedDate = rows.getFirst(); + + UUID uuid = customerPhoneUUID.get(phoneNumber); + updatedCustomer = customerRepository.findById(uuid).orElseThrow(); + assertEquals(updatedDate.get("numeroTelephone"), updatedCustomer.getPhoneNumber()); + assertEquals(updatedDate.get("prenom"), updatedCustomer.getFirstName()); + assertEquals(updatedDate.get("nom"), updatedCustomer.getLastName()); + } + + @And("the loyalty points remain unchanged at {int}") + public void theLoyaltyPointsRemainUnchangedAt(int expectedLoyaltyPoint) { + assertEquals(expectedLoyaltyPoint, updatedCustomer.getLoyaltyPoints()); + } + + @When("I delete the customer with phone number {string}") + public void iDeleteTheCustomerWithPhoneNumber(String phoneNumber) throws CustomerNotFoundException { + UUID uuid = customerPhoneUUID.get(phoneNumber); + customerUseCase.deleteCustomer(uuid); + } + + @Then("the customer {string} is removed from the system") + public void theCustomerIsRemovedFromTheSystem(String phoneNumber) { + UUID uuid = customerPhoneUUID.get(phoneNumber); + assertFalse(customerRepository.existsById(uuid)); + } + + @When("I add {int} loyalty points to customer {string}") + public void iAddLoyaltyPointsToCustomer(int loyaltyPointToAdd, String phoneNumber) throws CustomerNotFoundException { + UUID uuid = customerPhoneUUID.get(phoneNumber); + customerUseCase.addLoyaltyPoints(uuid, loyaltyPointToAdd); + } + + @Then("customer {string} now has {int} loyalty points") + public void customerNowHasLoyaltyPoints(String phoneNumber, int expectedLoyaltyPoints) { + UUID uuid = customerPhoneUUID.get(phoneNumber); + Customer customer = customerRepository.findById(uuid).orElseThrow(); + assertEquals(expectedLoyaltyPoints, customer.getLoyaltyPoints()); + } + + @When("I deduct {int} loyalty points from customer {string} for a purchase") + public void iDeductLoyaltyPointsFromCustomerForAPurchase(int pointsToRemove, String phoneNumber) throws CustomerNotFoundException, IllegalCustomerPointException { + UUID uuid = customerPhoneUUID.get(phoneNumber); + customerUseCase.subtractLoyaltyPoints(uuid, pointsToRemove); + } + + @When("I try to create a new customer with the following information:") + public void iTryToCreateANewCustomerWithTheFollowingInformation(DataTable ddataTable) { + List<Map<String, String>> rows = ddataTable.asMaps(String.class, String.class); + + Map<String, String> customerInfo = rows.getFirst(); + + CustomerInfo newCustomer = new CustomerInfo( + customerInfo.get("prenom"), + customerInfo.get("nom"), + customerInfo.get("numeroTelephone") + ); + + notValidCustomerException = assertThrows(NotValidCustomerException.class, () -> customerUseCase.registerCustomer(newCustomer)); + } + + @Then("the creation fails") + public void theCreationFails() { + assertNotNull(notValidCustomerException); + } + + + @And("I receive an error for validation customer message containing {string}") + public void iReceiveAnErrorMessageContaining(String errorMessage) { + assertEquals(errorMessage, notValidCustomerException.getMessage()); + } + + @And("the system still has {int} customers") + public void theSystemStillHasCustomers(int expectedNumberOfUser) { + assertEquals(expectedNumberOfUser, customerRepository.findAll().size()); + } + + @When("I try to deduct {int} loyalty points from customer {string} for a purchase") + public void iTryToDeductLoyaltyPointsFromCustomerForAPurchase(int points, String phoneNumber) { + UUID uuid = customerPhoneUUID.get(phoneNumber); + illegalCustomerPointException = assertThrows(IllegalCustomerPointException.class, () -> customerUseCase.subtractLoyaltyPoints(uuid, points)); + } + + @Then("the deduction fails") + public void theDeductionFails() { + assertNotNull(illegalCustomerPointException); + } + + @And("I receive an error for illegal customer exception message containing {string}") + public void iReceiveAnErrorForIllegalCustomerExceptionMessageContaining(String errorMessage) { + assertEquals(errorMessage, illegalCustomerPointException.getMessage()); + } + +} diff --git a/src/test/resources/features/client.feature b/src/test/resources/features/client.feature new file mode 100644 index 0000000..dd5be62 --- /dev/null +++ b/src/test/resources/features/client.feature @@ -0,0 +1,60 @@ +# language: en + +Feature: Manage customer accounts + + Background: + Given the system has the following customers: + | prenom | nom | numeroTelephone | pointsFidelite | + | Marie | Dupont | 0612345678 | 100 | + | Jean | Martin | 0687654321 | 50 | + | Sophie | Dubois | 0698765432 | 0 | + + Scenario: Create a new customer account + When I create a new customer with the following information: + | prenom | nom | numeroTelephone | + | Pierre | Lambert | 0611223344 | + Then a new customer is created + And the customer has 0 loyalty points + And the system now has 4 customers + + Scenario: Retrieve a customer by ID + When I request the customer with phone number "0612345678" + Then I receive the following customer information: + | prenom | nom | numeroTelephone | pointsFidelite | + | Marie | Dupont | 0612345678 | 100 | + + Scenario: Update a customer's information + When I update customer "0687654321" with the following information: + | prenom | nom | numeroTelephone | + | Jean | Bernard | 0666666666 | + Then the customer "0687654321" has the following updated information: + | prenom | nom | numeroTelephone | + | Jean | Bernard | 0666666666 | + And the loyalty points remain unchanged at 50 + + Scenario: Delete a customer + When I delete the customer with phone number "0698765432" + Then the customer "0698765432" is removed from the system + And the system now has 2 customers + + Scenario: Add loyalty points to a customer + When I add 25 loyalty points to customer "0687654321" + Then customer "0687654321" now has 75 loyalty points + + Scenario: Deduct loyalty points for a purchase + When I deduct 30 loyalty points from customer "0612345678" for a purchase + Then customer "0612345678" now has 70 loyalty points + + Scenario: Attempt to create a customer with invalid phone number + When I try to create a new customer with the following information: + | prenom | nom | numeroTelephone | + | Thomas | Petit | abcdefgh | + Then the creation fails + And I receive an error for validation customer message containing "Phone number is not valid" + And the system still has 3 customers + + Scenario: Attempt to deduct more loyalty points than available + When I try to deduct 150 loyalty points from customer "0687654321" for a purchase + Then the deduction fails + And I receive an error for illegal customer exception message containing "Cannot remove 150 points from 50 points" + And customer "0687654321" now has 50 loyalty points \ No newline at end of file