diff --git a/.idea/.gitignore b/.idea/.gitignore index 26d3352..13566b8 100644 --- a/.idea/.gitignore +++ b/.idea/.gitignore @@ -1,3 +1,8 @@ # Default ignored files /shelf/ /workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 9dc782b..5ddb3b3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,5 +8,5 @@ - + \ No newline at end of file diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionDTO.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionDTO.java new file mode 100644 index 0000000..6447380 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionDTO.java @@ -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; +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionInfo.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionInfo.java new file mode 100644 index 0000000..2bf5638 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/SubscriptionInfo.java @@ -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) { + +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/converter/SubscriptionConverter.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/converter/SubscriptionConverter.java new file mode 100644 index 0000000..9f8aaca --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/converter/SubscriptionConverter.java @@ -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(); + } + +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/entity/Subscription.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/entity/Subscription.java new file mode 100644 index 0000000..7d0efe3 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/entity/Subscription.java @@ -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(); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/NotValidSubscriptionException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/NotValidSubscriptionException.java new file mode 100644 index 0000000..4ab97d2 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/NotValidSubscriptionException.java @@ -0,0 +1,8 @@ +package fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception; + +public class NotValidSubscriptionException extends Exception { + + public NotValidSubscriptionException(String message) { + super(message); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/SubscriptionNotFoundException.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/SubscriptionNotFoundException.java new file mode 100644 index 0000000..26f1e1b --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/exception/SubscriptionNotFoundException.java @@ -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)); + } +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/repository/SubscriptionRepository.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/repository/SubscriptionRepository.java new file mode 100644 index 0000000..5d4e682 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/repository/SubscriptionRepository.java @@ -0,0 +1,36 @@ +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 subscriptions = new ArrayList<>(); + + public List findAll() { + return subscriptions; + } + + public Subscription save(Subscription newSubscription) { + Optional optionalSubscriptionWithSameId = this.findByCustomerId(newSubscription.getCustomerId()); + optionalSubscriptionWithSameId.ifPresentOrElse(subscriptions::remove, newSubscription::setRandomUUID); + this.subscriptions.add(newSubscription); + return newSubscription; + } + + public Optional findByCustomerId(UUID uuid) { + return this.subscriptions.stream() + .filter(subscription -> subscription.getCustomerId().equals(uuid)) + .findFirst(); + } + + public boolean existsById(UUID uuid) { + return this.subscriptions.stream() + .anyMatch(subscription -> subscription.getId().equals(uuid)); + } + +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/usecase/SubscriptionUseCase.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/usecase/SubscriptionUseCase.java new file mode 100644 index 0000000..a8c5e24 --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/usecase/SubscriptionUseCase.java @@ -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 findSubscriptionByCustomerId(UUID customerId) { + Optional 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); + } + +} diff --git a/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/validator/SubscriptionValidator.java b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/validator/SubscriptionValidator.java new file mode 100644 index 0000000..51112ec --- /dev/null +++ b/src/main/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/validator/SubscriptionValidator.java @@ -0,0 +1,38 @@ +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 fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.NotValidSubscriptionException { + validateDuration(newSubscription); + validatePaymentMethod(newSubscription); + } + + + private static void validateDuration(SubscriptionInfo newSubscription) throws fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.NotValidSubscriptionException { + if (newSubscription.duration() == null) { + throw new fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.NotValidSubscriptionException(DURATION_CANNOT_BE_NULL); + } + } + + private static void validatePaymentMethod(SubscriptionInfo newSubscription) throws fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception.NotValidSubscriptionException { + if (newSubscription.paymentMethod().isBlank()) { + throw new NotValidSubscriptionException(PAYMENT_METHOD_CANNOT_BE_BLANK); + } + } + +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/subscription/SubscriptionSteps.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/subscription/SubscriptionSteps.java new file mode 100644 index 0000000..eb52963 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/features/subscription/SubscriptionSteps.java @@ -0,0 +1,129 @@ +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.entity.Subscription; +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 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; + + +public class SubscriptionSteps { + + private final SubscriptionRepository subscriptionRepository = new SubscriptionRepository(); + private final SubscriptionUseCase subscriptionUseCase = new SubscriptionUseCase(subscriptionRepository); + private NotValidSubscriptionException notValidSubscriptionException; + + private final CustomerRepository customerRepository = new CustomerRepository(); + private UUID subscriptionRegistration; + private final Map 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> customers = dataTable.asMaps(String.class, String.class); + + for (Map 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> rows = dataTable.asMaps(String.class, String.class); + + // Extract the first row of data + Map 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() { + + } + + @When("I create a new subscription with Paypal:") + public void iCreateANewSubscriptionWithPaypal(DataTable dataTable) throws NotValidSubscriptionException { + List> rows = dataTable.asMaps(String.class, String.class); + + // Extract the first row of data + Map 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> rows = dataTable.asMaps(String.class, String.class); + + Map 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()); + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/converter/SubscriptionConverterTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/converter/SubscriptionConverterTest.java new file mode 100644 index 0000000..0fc7129 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/converter/SubscriptionConverterTest.java @@ -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()); + } + } +} + + diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/entity/SubscriptionTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/entity/SubscriptionTest.java new file mode 100644 index 0000000..ea79f4d --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/entity/SubscriptionTest.java @@ -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()); + } + +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/repository/SubscriptionRepositoryTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/repository/SubscriptionRepositoryTest.java new file mode 100644 index 0000000..83ea5b2 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/repository/SubscriptionRepositoryTest.java @@ -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("123e4567-e89b-12d3-a456-426614174000")) + .duration(24) + .paymentMethod("Paypal") + .build(); + Subscription2.setRandomUUID(); + } + + @Test + @DisplayName("New repository should be empty") + void testNewRepositoryIsEmpty() { + List 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 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 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 foundSubscription = repository.findByCustomerId(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 foundSubscription = repository.findByCustomerId(nonExistentId); + + assertTrue(foundSubscription.isEmpty()); + } + + @Test + @DisplayName("FindByCustomerId should return customer with matching customer id") + void testFindByCustomerId() { + Optional 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 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); + } + } +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/usecase/SubscribeUseCaseTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/usecase/SubscribeUseCaseTest.java new file mode 100644 index 0000000..85eba94 --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/usecase/SubscribeUseCaseTest.java @@ -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 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 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")); + } + } + +} diff --git a/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/validator/SubscriptionValidatorTest.java b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/validator/SubscriptionValidatorTest.java new file mode 100644 index 0000000..7343e8c --- /dev/null +++ b/src/test/java/fr/iut_fbleau/but3/dev62/mylibrary/subscription/validator/SubscriptionValidatorTest.java @@ -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 blank") + void testValidateBlankCustomerId() { + SubscriptionInfo subscriptionWithBlankCustomerId = new SubscriptionInfo(null, 12, "CB"); + + NotValidSubscriptionException exception = assertThrows( + NotValidSubscriptionException.class, + () -> SubscriptionValidator.validate(subscriptionWithBlankCustomerId) + ); + + 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()); + } + } +} diff --git a/src/test/resources/features/subscription.feature b/src/test/resources/features/subscription.feature new file mode 100644 index 0000000..ea84780 --- /dev/null +++ b/src/test/resources/features/subscription.feature @@ -0,0 +1,28 @@ +# language: en + +Feature: Manage customer subscription + Background: + Given the system has the following customers: + | customerId | firstName | lastName | phoneNumer | pointsFidelite | + | 11111111-1111-1111-1111-111111111111 | John | Doe | 0612345678 | 100 | + | 22222222-2222-2222-2222-222222222222 | Bob | Dupond | 0687654321 | 50 | + | 33333333-3333-3333-3333-333333333333 | Alice | Untel | 0698765432 | 0 | + + Scenario: Create a new subscription + When I create a new subscription with CB: + | customerId | duration | paymentMethod | + | 11111111-1111-1111-1111-111111111111 | 12 | CB | + Then a new subscription is created + + Scenario: Create a new subscription + When I create a new subscription with Paypal: + | customerId | duration | paymentMethod | + | 22222222-2222-2222-2222-222222222222 | 24 | Paypal | + Then a new subscription is created + + Scenario: Attempt to create a subscription with invalid duration: + When I try to create a new subscription with the following information: + | customerId | duration | paymentMethod | + | 33333333-3333-3333-3333-333333333333 | 0 | CB | + Then the subsription duration creation fails + And I receive an error for validation subscription message containing "Duration must be positive" \ No newline at end of file