diff --git a/README.md b/README.md index 034b949..0b4d69e 100644 --- a/README.md +++ b/README.md @@ -146,3 +146,42 @@ docker compose up --build | `MAX_FILE_SIZE` / `MAX_REQUEST_SIZE` | Límites upload | `50MB` | | `LOG_LEVEL_ROOT` / `LOG_LEVEL_OUTFITLAB` | Logging | `INFO` / `DEBUG` | | `SERVER_PORT` | Puerto interno | `8080` | + +## Testing +Utilizacion de Patron Given - When - Then +## - Given: preparar datos y mocks. Ej: + + private UserSubscriptionEntity givenExistingSubscriptionReturningEntity(String email, String planCode, int combinations) { + UserSubscriptionEntity entity = givenSubscriptionEntity(email, planCode, 10L, combinations); + when(userSubscriptionJpaRepository.findByUserEmail(email)) + .thenReturn(Optional.of(entity)); + return entity; + } +## - WHEN: ejecutar el método a probar. Ej: + + private void whenFindingSubscriptionByUserEmail(String email, UserSubscriptionEntity entity) { + when(userSubscriptionJpaRepository.findByUserEmail(email)).thenReturn(Optional.of(entity)); + } + +## - THEN: verificar resultados o excepciones. Ej: + + private void thenSubscriptionShouldHaveCombinationsUsed(UserSubscriptionModel model, int expected) { + assertThat(model).isNotNull(); + assertThat(model.getCombinationsUsed()).isEqualTo(expected); + } + +## - Test completo del metodo + @Test + void shouldReturnSubscriptionModelWhenEmailExists() throws SubscriptionNotFoundException { + String email = "test@mail.com"; + UserSubscriptionEntity entity = givenExistingSubscriptionReturningEntity(email, "BASIC", 10); + + whenFindingSubscriptionByUserEmail(email, entity); + + UserSubscriptionModel result = whenFindByUserEmail(email); + + thenSubscriptionShouldHaveCombinationsUsed(result, 10); + verify(userSubscriptionJpaRepository).findByUserEmail(email); + } + + diff --git a/src/main/java/com/outfitlab/project/domain/interfaces/repositories/PrendaOcacionRepository.java b/src/main/java/com/outfitlab/project/domain/interfaces/repositories/PrendaOcacionRepository.java deleted file mode 100644 index d89cf62..0000000 --- a/src/main/java/com/outfitlab/project/domain/interfaces/repositories/PrendaOcacionRepository.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.outfitlab.project.domain.interfaces.repositories; - -public interface PrendaOcacionRepository { - - void deleteAllPrendaOcacionByGarment(String garmentCode); -} diff --git a/src/main/java/com/outfitlab/project/domain/interfaces/repositories/PrendaOcasionRepository.java b/src/main/java/com/outfitlab/project/domain/interfaces/repositories/PrendaOcasionRepository.java new file mode 100644 index 0000000..2324506 --- /dev/null +++ b/src/main/java/com/outfitlab/project/domain/interfaces/repositories/PrendaOcasionRepository.java @@ -0,0 +1,6 @@ +package com.outfitlab.project.domain.interfaces.repositories; + +public interface PrendaOcasionRepository { + + void deleteAllPrendaOcasionByGarment(String garmentCode); +} diff --git a/src/main/java/com/outfitlab/project/domain/useCases/recomendations/DeleteAllPrendaOcacionRelatedToGarment.java b/src/main/java/com/outfitlab/project/domain/useCases/recomendations/DeleteAllPrendaOcacionRelatedToGarment.java index cb4c166..8afcf97 100644 --- a/src/main/java/com/outfitlab/project/domain/useCases/recomendations/DeleteAllPrendaOcacionRelatedToGarment.java +++ b/src/main/java/com/outfitlab/project/domain/useCases/recomendations/DeleteAllPrendaOcacionRelatedToGarment.java @@ -1,16 +1,16 @@ package com.outfitlab.project.domain.useCases.recomendations; -import com.outfitlab.project.domain.interfaces.repositories.PrendaOcacionRepository; +import com.outfitlab.project.domain.interfaces.repositories.PrendaOcasionRepository; public class DeleteAllPrendaOcacionRelatedToGarment { - private final PrendaOcacionRepository prendaOcacionRepository; + private final PrendaOcasionRepository prendaOcacionRepository; - public DeleteAllPrendaOcacionRelatedToGarment(PrendaOcacionRepository prendaOcacionRepository){ + public DeleteAllPrendaOcacionRelatedToGarment(PrendaOcasionRepository prendaOcacionRepository){ this.prendaOcacionRepository = prendaOcacionRepository; } public void execute(String garmentCode){ - this.prendaOcacionRepository.deleteAllPrendaOcacionByGarment(garmentCode); + this.prendaOcacionRepository.deleteAllPrendaOcasionByGarment(garmentCode); } } diff --git a/src/main/java/com/outfitlab/project/infrastructure/config/GarmentConfig.java b/src/main/java/com/outfitlab/project/infrastructure/config/GarmentConfig.java index 8dcee9e..39b0ab3 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/config/GarmentConfig.java +++ b/src/main/java/com/outfitlab/project/infrastructure/config/GarmentConfig.java @@ -19,7 +19,7 @@ public class GarmentConfig { @Bean public GarmentRepository garmentRepository(GarmentJpaRepository jpaRepository, BrandJpaRepository brandJpaRepository, ClimaJpaRepository climaJpaRepository, ColorJpaRepository colorJpaRepository, - OcacionJpaRepository ocacionJpaRepository) { + OcasionJpaRepository ocacionJpaRepository) { return new GarmentRepositoryImpl(jpaRepository, brandJpaRepository, colorJpaRepository, climaJpaRepository, ocacionJpaRepository); } @@ -41,7 +41,7 @@ public DeleteAllFavoritesRelatedToGarment deleteAllFavoritesRelatedToGarment(Use } @Bean - public DeleteAllPrendaOcacionRelatedToGarment deleteAllPrendaOcacionRelatedToGarment(PrendaOcacionRepository prendaOcacionRepository){ + public DeleteAllPrendaOcacionRelatedToGarment deleteAllPrendaOcacionRelatedToGarment(PrendaOcasionRepository prendaOcacionRepository){ return new DeleteAllPrendaOcacionRelatedToGarment(prendaOcacionRepository); } diff --git a/src/main/java/com/outfitlab/project/infrastructure/config/RecomendationConfig.java b/src/main/java/com/outfitlab/project/infrastructure/config/RecomendationConfig.java index efe6061..8689c51 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/config/RecomendationConfig.java +++ b/src/main/java/com/outfitlab/project/infrastructure/config/RecomendationConfig.java @@ -27,8 +27,8 @@ public DeleteGarmentRecomentationsRelatedToGarment deleteGarmentRecomentationsRe } @Bean - public PrendaOcacionRepository prendaOcacionRepository(PrendaOcacionJpaRepository prendaOcacionJpaRepository){ - return new PrendaOcacionRepositoryImpl(prendaOcacionJpaRepository); + public PrendaOcasionRepository prendaOcacionRepository(PrendaOcasionJpaRepository prendaOcacionJpaRepository){ + return new PrendaOcasionRepositoryImpl(prendaOcacionJpaRepository); } @Bean @@ -42,7 +42,7 @@ public CreateSugerenciasByGarmentsCode createSugerenciasByGarmentsCode(GarmentRe } @Bean - public DeleteAllPrendaOcacionRelatedToGarment deleteAllPrendaOcacionRelatedToGarment(PrendaOcacionRepository prendaOcacionRepository){ + public DeleteAllPrendaOcacionRelatedToGarment deleteAllPrendaOcacionRelatedToGarment(PrendaOcasionRepository prendaOcacionRepository){ return new DeleteAllPrendaOcacionRelatedToGarment(prendaOcacionRepository); } @@ -52,8 +52,8 @@ public ColorRepository colorRepository(ColorJpaRepository colorJpaRepository){ } @Bean - public OcacionRepository ocacionRepository(OcacionJpaRepository ocacionJpaRepository){ - return new OcacionRepositoryImpl(ocacionJpaRepository); + public OcacionRepository ocacionRepository(OcasionJpaRepository ocacionJpaRepository){ + return new OcasionRepositoryImpl(ocacionJpaRepository); } @Bean diff --git a/src/main/java/com/outfitlab/project/infrastructure/model/MarcaEntity.java b/src/main/java/com/outfitlab/project/infrastructure/model/MarcaEntity.java index 0510472..d1f8cd3 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/model/MarcaEntity.java +++ b/src/main/java/com/outfitlab/project/infrastructure/model/MarcaEntity.java @@ -40,7 +40,10 @@ public class MarcaEntity { private String urlSite; public MarcaEntity(){} - + public MarcaEntity(String codigoMarca, String nombre) { + this.codigoMarca = codigoMarca; + this.nombre = nombre; + } public MarcaEntity(String codigoMarca, String nombre, String logoUrl, LocalDateTime createdAt, LocalDateTime updatedAt) { this.codigoMarca = codigoMarca; this.nombre = nombre; diff --git a/src/main/java/com/outfitlab/project/infrastructure/model/SubscriptionEntity.java b/src/main/java/com/outfitlab/project/infrastructure/model/SubscriptionEntity.java index 07be44c..17d28a6 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/model/SubscriptionEntity.java +++ b/src/main/java/com/outfitlab/project/infrastructure/model/SubscriptionEntity.java @@ -47,6 +47,11 @@ public class SubscriptionEntity { @Column(name = "has_advanced_reports", columnDefinition = "boolean default false") private boolean hasAdvancedReports = false; + public SubscriptionEntity(String name, String planCode) { + this.name = name; + this.planCode = planCode; + } + public Long getId() { return id; } diff --git a/src/main/java/com/outfitlab/project/infrastructure/repositories/ColorRepositoryImpl.java b/src/main/java/com/outfitlab/project/infrastructure/repositories/ColorRepositoryImpl.java index a6d9b9c..816a8e9 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/repositories/ColorRepositoryImpl.java +++ b/src/main/java/com/outfitlab/project/infrastructure/repositories/ColorRepositoryImpl.java @@ -21,7 +21,7 @@ public ColorRepositoryImpl(ColorJpaRepository colorJpaRepository) { @Override public List findAllColores() { List colors = this.colorJpaRepository.findAll(); - if (colors.isEmpty()) throw new ColorNotFoundException("No encontramos ocaciones."); + if (colors.isEmpty()) throw new ColorNotFoundException("No encontramos ocasiones."); return colors.stream().map(ColorEntity::convertEntityToModel) .toList(); } diff --git a/src/main/java/com/outfitlab/project/infrastructure/repositories/GarmentRepositoryImpl.java b/src/main/java/com/outfitlab/project/infrastructure/repositories/GarmentRepositoryImpl.java index 8cfb63d..1d024ec 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/repositories/GarmentRepositoryImpl.java +++ b/src/main/java/com/outfitlab/project/infrastructure/repositories/GarmentRepositoryImpl.java @@ -1,7 +1,6 @@ package com.outfitlab.project.infrastructure.repositories; import com.outfitlab.project.domain.exceptions.GarmentNotFoundException; -import com.outfitlab.project.domain.interfaces.repositories.ClimaRepository; import com.outfitlab.project.domain.interfaces.repositories.GarmentRepository; import com.outfitlab.project.domain.model.ClimaModel; import com.outfitlab.project.domain.model.ColorModel; @@ -25,9 +24,9 @@ public class GarmentRepositoryImpl implements GarmentRepository { private final BrandJpaRepository brandJpaRepository; private final ColorJpaRepository colorJpaRepository; private final ClimaJpaRepository climaJpaRepository; - private final OcacionJpaRepository ocacionJpaRepository; + private final OcasionJpaRepository ocacionJpaRepository; - public GarmentRepositoryImpl(GarmentJpaRepository garmentJpaRepository, BrandJpaRepository brandJpaRepository, ColorJpaRepository colorJpaRepository, ClimaJpaRepository climaJpaRepository, OcacionJpaRepository ocacionJpaRepository) { + public GarmentRepositoryImpl(GarmentJpaRepository garmentJpaRepository, BrandJpaRepository brandJpaRepository, ColorJpaRepository colorJpaRepository, ClimaJpaRepository climaJpaRepository, OcasionJpaRepository ocacionJpaRepository) { this.garmentJpaRepository = garmentJpaRepository; this.brandJpaRepository = brandJpaRepository; this.colorJpaRepository = colorJpaRepository; diff --git a/src/main/java/com/outfitlab/project/infrastructure/repositories/GeminiClientImpl.java b/src/main/java/com/outfitlab/project/infrastructure/repositories/GeminiClientImpl.java index 2433e6a..020dfa4 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/repositories/GeminiClientImpl.java +++ b/src/main/java/com/outfitlab/project/infrastructure/repositories/GeminiClientImpl.java @@ -104,7 +104,7 @@ private String buildRequestBody(String peticionUsuario) throws com.fasterxml.jac return objectMapper.writeValueAsString(directRequest); } - private String extractJsonContent(String apiResponseBody) throws Exception { + protected String extractJsonContent(String apiResponseBody) throws Exception { Map responseMap = objectMapper.readValue(apiResponseBody, Map.class); List> candidates = (List>) responseMap.get("candidates"); diff --git a/src/main/java/com/outfitlab/project/infrastructure/repositories/OcacionRepositoryImpl.java b/src/main/java/com/outfitlab/project/infrastructure/repositories/OcasionRepositoryImpl.java similarity index 60% rename from src/main/java/com/outfitlab/project/infrastructure/repositories/OcacionRepositoryImpl.java rename to src/main/java/com/outfitlab/project/infrastructure/repositories/OcasionRepositoryImpl.java index bffcb9a..585cff1 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/repositories/OcacionRepositoryImpl.java +++ b/src/main/java/com/outfitlab/project/infrastructure/repositories/OcasionRepositoryImpl.java @@ -1,26 +1,25 @@ package com.outfitlab.project.infrastructure.repositories; -import com.outfitlab.project.domain.exceptions.ClimaNotFoundException; import com.outfitlab.project.domain.exceptions.OcasionNotFoundException; import com.outfitlab.project.domain.interfaces.repositories.OcacionRepository; import com.outfitlab.project.domain.model.OcasionModel; -import com.outfitlab.project.infrastructure.model.ClimaEntity; import com.outfitlab.project.infrastructure.model.OcasionEntity; -import com.outfitlab.project.infrastructure.repositories.interfaces.OcacionJpaRepository; +import com.outfitlab.project.infrastructure.repositories.interfaces.OcasionJpaRepository; import java.util.List; -public class OcacionRepositoryImpl implements OcacionRepository { +public class OcasionRepositoryImpl implements OcacionRepository { - private final OcacionJpaRepository ocacionJpaRepository; - public OcacionRepositoryImpl(OcacionJpaRepository ocacionJpaRepository) { - this.ocacionJpaRepository = ocacionJpaRepository; + private final OcasionJpaRepository ocasionJpaRepository; + + public OcasionRepositoryImpl(OcasionJpaRepository ocasionJpaRepository) { + this.ocasionJpaRepository = ocasionJpaRepository; } @Override public List findAllOcasiones() { - List ocaciones = this.ocacionJpaRepository.findAll(); + List ocaciones = this.ocasionJpaRepository.findAll(); if (ocaciones.isEmpty()) throw new OcasionNotFoundException("No encontramos ocaciones."); return ocaciones.stream().map(OcasionEntity::convertEntityToModel) .toList(); diff --git a/src/main/java/com/outfitlab/project/infrastructure/repositories/PrendaOcacionRepositoryImpl.java b/src/main/java/com/outfitlab/project/infrastructure/repositories/PrendaOcacionRepositoryImpl.java deleted file mode 100644 index 0475ae1..0000000 --- a/src/main/java/com/outfitlab/project/infrastructure/repositories/PrendaOcacionRepositoryImpl.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.outfitlab.project.infrastructure.repositories; - -import com.outfitlab.project.domain.interfaces.repositories.PrendaOcacionRepository; -import com.outfitlab.project.infrastructure.repositories.interfaces.PrendaOcacionJpaRepository; - -public class PrendaOcacionRepositoryImpl implements PrendaOcacionRepository { - - private final PrendaOcacionJpaRepository prendaOcacionJpaRepository; - - public PrendaOcacionRepositoryImpl(PrendaOcacionJpaRepository prendaOcacionJpaRepository){ - this.prendaOcacionJpaRepository = prendaOcacionJpaRepository; - } - - @Override - public void deleteAllPrendaOcacionByGarment(String garmentCode) { - this.prendaOcacionJpaRepository.deleteAllByGarmentCode(garmentCode); - } -} diff --git a/src/main/java/com/outfitlab/project/infrastructure/repositories/PrendaOcasionRepositoryImpl.java b/src/main/java/com/outfitlab/project/infrastructure/repositories/PrendaOcasionRepositoryImpl.java new file mode 100644 index 0000000..0e527eb --- /dev/null +++ b/src/main/java/com/outfitlab/project/infrastructure/repositories/PrendaOcasionRepositoryImpl.java @@ -0,0 +1,18 @@ +package com.outfitlab.project.infrastructure.repositories; + +import com.outfitlab.project.domain.interfaces.repositories.PrendaOcasionRepository; +import com.outfitlab.project.infrastructure.repositories.interfaces.PrendaOcasionJpaRepository; + +public class PrendaOcasionRepositoryImpl implements PrendaOcasionRepository { + + private final PrendaOcasionJpaRepository prendaOcasionJpaRepository; + + public PrendaOcasionRepositoryImpl(PrendaOcasionJpaRepository prendaOcasionJpaRepository){ + this.prendaOcasionJpaRepository = prendaOcasionJpaRepository; + } + + @Override + public void deleteAllPrendaOcasionByGarment(String garmentCode) { + this.prendaOcasionJpaRepository.deleteAllByGarmentCode(garmentCode); + } +} diff --git a/src/main/java/com/outfitlab/project/infrastructure/repositories/TripoRepositoryImpl.java b/src/main/java/com/outfitlab/project/infrastructure/repositories/TripoRepositoryImpl.java index 2ff8284..f14d037 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/repositories/TripoRepositoryImpl.java +++ b/src/main/java/com/outfitlab/project/infrastructure/repositories/TripoRepositoryImpl.java @@ -145,7 +145,7 @@ private void checkIfExtensionIsValid(String extension) { } } - private MultipartFile convertImageUrlToMultipartFile(String imageUrl) throws ErrorUploadImageToTripoException { + protected MultipartFile convertImageUrlToMultipartFile(String imageUrl) throws ErrorUploadImageToTripoException { InputStream inputStream = null; try { URL url = new URL(imageUrl); @@ -233,7 +233,7 @@ private Map getHttpBodyImageToModelTripo(Map upl return bodyMap; } - private HttpHeaders getHttpHeaders(MediaType type) { + protected HttpHeaders getHttpHeaders(MediaType type) { HttpHeaders taskHeaders = new HttpHeaders(); taskHeaders.setContentType(type); taskHeaders.setBearerAuth(tripoApiKey); @@ -267,7 +267,7 @@ private static void checkIfResponseIsOk(ResponseEntity response) throws } } - private ResponseEntity generateRequestToUploadImageToTripo(MultipartFile imageFile, String originalFilename) throws ErroBytesException { + protected ResponseEntity generateRequestToUploadImageToTripo(MultipartFile imageFile, String originalFilename) throws ErroBytesException { MultiValueMap body = new LinkedMultiValueMap<>(); body.add("file", tryGetByteArrayResourceFromImage(imageFile, originalFilename)); HttpHeaders headers = getHttpHeaders(MediaType.MULTIPART_FORM_DATA); @@ -287,7 +287,7 @@ private String tryGetImageToken(ResponseEntity response) throws ErrorRea return imageToken; } - private ResponseEntity requestTripoTaskStatus(String taskId, HttpEntity entityWithTaskHeaders) { + protected ResponseEntity requestTripoTaskStatus(String taskId, HttpEntity entityWithTaskHeaders) { return restTemplate.exchange( taskUrl + "/" + taskId, HttpMethod.GET, diff --git a/src/main/java/com/outfitlab/project/infrastructure/repositories/UploadImageRepositoryImpl.java b/src/main/java/com/outfitlab/project/infrastructure/repositories/UploadImageRepositoryImpl.java index 42fee9f..ae4c8cb 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/repositories/UploadImageRepositoryImpl.java +++ b/src/main/java/com/outfitlab/project/infrastructure/repositories/UploadImageRepositoryImpl.java @@ -24,6 +24,13 @@ public class UploadImageRepositoryImpl implements com.outfitlab.project.domain.i @Value("${AWS_BUCKET_NAME}") private String bucketName; + public UploadImageRepositoryImpl(S3Client s3Client, String region, String bucketName) { + this.s3Client = s3Client; + this.region = region; + this.bucketName = bucketName; + } + + @Override public String uploadFile(MultipartFile file, String folder) { try { @@ -73,4 +80,8 @@ private String extractKeyFromUrl(String url) { if (index == -1) {throw new IllegalArgumentException("URL de S3 inválida: " + url);} return url.substring(index + ".amazonaws.com/".length()); } + + + public void setRegion(String region) { this.region = region; } + public void setBucketName(String bucketName) { this.bucketName = bucketName; } } diff --git a/src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/OcacionJpaRepository.java b/src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/OcasionJpaRepository.java similarity index 85% rename from src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/OcacionJpaRepository.java rename to src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/OcasionJpaRepository.java index b09de9a..575d4df 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/OcacionJpaRepository.java +++ b/src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/OcasionJpaRepository.java @@ -5,7 +5,7 @@ import java.util.Optional; -public interface OcacionJpaRepository extends JpaRepository { +public interface OcasionJpaRepository extends JpaRepository { Optional findByNombre(String nombre); Optional findOcasionEntityByNombre(String nombre); diff --git a/src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/PrendaOcacionJpaRepository.java b/src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/PrendaOcasionJpaRepository.java similarity index 91% rename from src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/PrendaOcacionJpaRepository.java rename to src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/PrendaOcasionJpaRepository.java index 53cba56..385a7bd 100644 --- a/src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/PrendaOcacionJpaRepository.java +++ b/src/main/java/com/outfitlab/project/infrastructure/repositories/interfaces/PrendaOcasionJpaRepository.java @@ -7,7 +7,7 @@ import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; -public interface PrendaOcacionJpaRepository extends JpaRepository { +public interface PrendaOcasionJpaRepository extends JpaRepository { @Transactional @Modifying @Query(""" diff --git a/src/main/java/com/outfitlab/project/presentation/dto/StatusResponse.java b/src/main/java/com/outfitlab/project/presentation/dto/StatusResponse.java index 12fa8d1..af149d1 100644 --- a/src/main/java/com/outfitlab/project/presentation/dto/StatusResponse.java +++ b/src/main/java/com/outfitlab/project/presentation/dto/StatusResponse.java @@ -1,7 +1,9 @@ package com.outfitlab.project.presentation.dto; -import java.util.List; +import lombok.AllArgsConstructor; +import java.util.List; +@AllArgsConstructor public class StatusResponse { private String id; private String status; diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/BrandRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/BrandRepositoryImplTest.java index e02e1fb..c26164a 100644 --- a/src/test/java/com/outfitlab/project/infrastructure/repositories/BrandRepositoryImplTest.java +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/BrandRepositoryImplTest.java @@ -1,42 +1,131 @@ package com.outfitlab.project.infrastructure.repositories; -import com.outfitlab.project.domain.interfaces.repositories.BrandRepository; +import com.outfitlab.project.domain.exceptions.BrandsNotFoundException; +import com.outfitlab.project.domain.model.BrandModel; import com.outfitlab.project.infrastructure.model.MarcaEntity; import com.outfitlab.project.infrastructure.repositories.interfaces.BrandJpaRepository; + import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; +import org.mockito.InjectMocks; +import org.mockito.Mock; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.annotation.Rollback; -import org.springframework.transaction.annotation.Transactional; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; import org.springframework.test.context.ActiveProfiles; -import static org.junit.jupiter.api.Assertions.*; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + + +import static org.mockito.Mockito.*; + + @SpringBootTest @ActiveProfiles("test") // IMPORTANTISIMO GENTE ESTE ACTIVATE PROFILE PARA NO AFECTAR LA BDD DE RENDER, PRESTAR ATENCIÓN class BrandRepositoryImplTest { - @Autowired + @Mock private BrandJpaRepository brandJpaRepository; - @Autowired - private BrandRepository brandRepository; + @InjectMocks + private BrandRepositoryImpl repository; @Test - @Transactional - @Rollback - void testFindByBrandCode() { - MarcaEntity entity = new MarcaEntity(); - entity.setCodigoMarca("raviolito"); - entity.setNombre("Raviolito"); - entity.setLogoUrl("https://raviolito-logo.png"); - brandJpaRepository.save(entity); + void shouldReturnBrandModelWhenBrandCodeExists() { + givenExistingBrand("NIKE", "Nike"); - var result = brandRepository.findByBrandCode("raviolito"); + BrandModel result = whenFindByBrandCode("NIKE"); - assertNotNull(result); - assertEquals("raviolito", result.getCodigoMarca()); - assertEquals("Raviolito", result.getNombre()); + thenBrandShouldBe(result, "NIKE", "Nike"); + verify(brandJpaRepository).findByCodigoMarca("NIKE"); + } + + @Test + void shouldThrowExceptionWhenBrandCodeDoesNotExist() { + givenNonExistingBrand("XYZ"); + + assertThatThrownBy(() -> whenFindByBrandCode("XYZ")) + .isInstanceOf(BrandsNotFoundException.class) + .hasMessageContaining("No encontramos la marca."); + + verify(brandJpaRepository).findByCodigoMarca("XYZ"); + } + + @Test + void shouldReturnBrandsPageWhenGettingAllBrands() { + givenPageBrands("ADIDAS", "Adidas"); + + Page result = whenGetAllBrands(0); + + thenFirstBrandShouldBe(result, "ADIDAS"); + verify(brandJpaRepository).findAll(PageRequest.of(0, 10)); + } + + @Test + void shouldCreateBrandAndReturnCode() { + BrandModel model = givenBrandModel("PUMA", "Puma"); + + givenSavedBrand("PUMA", "Puma"); + + String result = whenCreateBrand(model); + + thenBrandCodeShouldBe(result, "PUMA"); + verify(brandJpaRepository).save(any(MarcaEntity.class)); + } + + private void givenExistingBrand(String code, String name) { + MarcaEntity entity = new MarcaEntity(code, name, "logo.png", "site.com"); + when(brandJpaRepository.findByCodigoMarca(code)).thenReturn(entity); + } + + private void givenNonExistingBrand(String code) { + when(brandJpaRepository.findByCodigoMarca(code)).thenReturn(null); + } + + private void givenPageBrands(String code, String name) { + MarcaEntity entity = new MarcaEntity(code, name, "logo2.png", "site.com"); + Page page = new PageImpl<>(List.of(entity)); + when(brandJpaRepository.findAll(PageRequest.of(0, 10))).thenReturn(page); + } + + private BrandModel givenBrandModel(String code, String name) { + return new BrandModel(code, name, "logo.png", "site.com"); + } + + private void givenSavedBrand(String code, String name) { + MarcaEntity saved = new MarcaEntity(code, name, "logo.png", "site.com"); + when(brandJpaRepository.save(any(MarcaEntity.class))).thenReturn(saved); + } + + private BrandModel whenFindByBrandCode(String code) { + return repository.findByBrandCode(code); + } + + private Page whenGetAllBrands(int page) { + return repository.getAllBrands(page); + } + + private String whenCreateBrand(BrandModel model) { + return repository.createBrand(model); + } + + private void thenBrandShouldBe(BrandModel result, String code, String name) { + assertThat(result).isNotNull(); + assertThat(result.getCodigoMarca()).isEqualTo(code); + assertThat(result.getNombre()).isEqualTo(name); + } + + private void thenFirstBrandShouldBe(Page page, String expectedCode) { + assertThat(page).isNotEmpty(); + assertThat(page.getContent().get(0).getCodigoMarca()).isEqualTo(expectedCode); + } + + private void thenBrandCodeShouldBe(String result, String expectedCode) { + assertThat(result).isEqualTo(expectedCode); } } + diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/ClimaRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/ClimaRepositoryImplTest.java new file mode 100644 index 0000000..224b7d8 --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/ClimaRepositoryImplTest.java @@ -0,0 +1,68 @@ +package com.outfitlab.project.infrastructure.repositories; + +import static org.assertj.core.api.Assertions.*; + +import com.outfitlab.project.domain.exceptions.ClimaNotFoundException; +import com.outfitlab.project.domain.model.ClimaModel; +import com.outfitlab.project.infrastructure.model.ClimaEntity; +import com.outfitlab.project.infrastructure.repositories.interfaces.ClimaJpaRepository; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; + +import static org.mockito.Mockito.*; +@SpringBootTest +@ActiveProfiles("test") +class ClimaRepositoryImplTest { + @Mock + private ClimaJpaRepository climaJpaRepository; + + @InjectMocks + private ClimaRepositoryImpl repository; + + @Test + void shouldReturnClimasWhenTheyExist() { + givenExistingClimas("FRIO"); + + var result = whenFindAllClimas(); + + thenFirstClimaShouldBe(result, "FRIO"); + verify(climaJpaRepository).findAll(); + } + + @Test + void shouldThrowExceptionWhenClimasListIsEmpty() { + givenEmptyClimas(); + + assertThatThrownBy(this::whenFindAllClimas) + .isInstanceOf(ClimaNotFoundException.class) + .hasMessageContaining("No encontramos climas."); + + verify(climaJpaRepository).findAll(); + } + + private void givenExistingClimas(String nombre) { + ClimaEntity clima = new ClimaEntity(); + clima.setId(1L); + clima.setNombre(nombre); + + when(climaJpaRepository.findAll()).thenReturn(List.of(clima)); + } + + private void givenEmptyClimas() { + when(climaJpaRepository.findAll()).thenReturn(List.of()); + } + + private List whenFindAllClimas() { + return repository.findAllClimas(); + } + + private void thenFirstClimaShouldBe(List result, String nombreEsperado) { + assertThat(result).isNotEmpty(); + assertThat(result.get(0).getNombre()).isEqualTo(nombreEsperado); + } +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/ColorRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/ColorRepositoryImplTest.java new file mode 100644 index 0000000..eb76930 --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/ColorRepositoryImplTest.java @@ -0,0 +1,66 @@ +package com.outfitlab.project.infrastructure.repositories; + +import com.outfitlab.project.domain.exceptions.ColorNotFoundException; +import com.outfitlab.project.domain.model.ColorModel; +import com.outfitlab.project.infrastructure.model.ColorEntity; +import com.outfitlab.project.infrastructure.repositories.interfaces.ColorJpaRepository; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; +@SpringBootTest +@ActiveProfiles("test") +class ColorRepositoryImplTest { + + @Mock + private ColorJpaRepository colorJpaRepository; + + @InjectMocks + private ColorRepositoryImpl repository; + + @Test + void shouldReturnColorsWhenTheyExist() { + givenExistingColors("ROJO"); + + var result = whenFindAllColores(); + + thenFirstColorShouldBe(result, "ROJO"); + verify(colorJpaRepository).findAll(); + } + + @Test + void shouldThrowExceptionWhenColorsListIsEmpty() { + givenEmptyColors(); + + assertThatThrownBy(this::whenFindAllColores) + .isInstanceOf(ColorNotFoundException.class) + .hasMessageContaining("No encontramos ocasiones."); + + verify(colorJpaRepository).findAll(); + } + private void givenExistingColors(String nombreColor) { + ColorEntity entity = new ColorEntity(); + entity.setId(1L); + entity.setNombre(nombreColor); + + when(colorJpaRepository.findAll()).thenReturn(List.of(entity)); + } + + private void givenEmptyColors() { + when(colorJpaRepository.findAll()).thenReturn(List.of()); + } + + private List whenFindAllColores() { + return repository.findAllColores(); + } + + private void thenFirstColorShouldBe(List result, String nombreEsperado) { + assertThat(result).isNotEmpty(); + assertThat(result.get(0).getNombre()).isEqualTo(nombreEsperado); + } +} diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/CombinationAttemptRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/CombinationAttemptRepositoryImplTest.java new file mode 100644 index 0000000..31dbf4d --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/CombinationAttemptRepositoryImplTest.java @@ -0,0 +1,275 @@ +package com.outfitlab.project.infrastructure.repositories; + +import com.outfitlab.project.domain.model.*; +import com.outfitlab.project.infrastructure.model.*; +import com.outfitlab.project.infrastructure.repositories.interfaces.CombinationAttemptJpaRepository; +import com.outfitlab.project.infrastructure.repositories.interfaces.CombinationJpaRepository; +import com.outfitlab.project.infrastructure.repositories.interfaces.UserJpaRepository; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; +@SpringBootTest +@ActiveProfiles("test") +class CombinationAttemptRepositoryImplTest { + @Mock + private CombinationAttemptJpaRepository combinationAttemptJpaRepository; + @Mock + private UserJpaRepository userJpaRepository; + @Mock + private CombinationJpaRepository combinationJpaRepository; + @InjectMocks + private CombinationAttemptRepositoryImpl repository; + + + @Test + void shouldSaveWhenUserExistsAndCombinationExistsById() { + CombinationAttemptModel model = givenModelWithUserAndCombinationId(1L, 10L); + + givenExistingUser(1L); + givenExistingCombination(10L); + givenSavedAttempt(99L); + + Long result = whenSave(model); + + thenAttemptIdShouldBe(result, 99L); + } + + @Test + void shouldSaveWhenCombinationFoundByPrendas() { + CombinationAttemptModel model = givenModelWithPrendas(1L, 2L); + + givenExistingUser(1L); + givenCombinationFoundByPrendas(1L, 2L, 50L); + givenSavedAttempt(77L); + + Long result = whenSave(model); + + thenAttemptIdShouldBe(result, 77L); + } + + @Test + void shouldSaveWhenCombinationNotFoundAndCreateNewOne() { + CombinationAttemptModel model = givenModelWithPrendas(1L, 2L); + + givenExistingUser(1L); + givenCombinationNotFoundByPrendas(); + givenCreatedCombination(200L); + givenSavedAttempt(88L); + + Long result = whenSave(model); + + thenAttemptIdShouldBe(result, 88L); + } + + @Test + void shouldThrowWhenUserDoesNotExist() { + CombinationAttemptModel model = givenModelWithUserAndCombinationId(999L, 10L); + + givenUserNotFound(999L); + + assertThatThrownBy(() -> whenSave(model)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("User not found"); + } + + @Test + void shouldThrowWhenCombinationIdDoesNotExist() { + CombinationAttemptModel model = givenModelWithUserAndCombinationId(1L, 500L); + + givenExistingUser(1L); + givenCombinationIdNotFound(500L); + + assertThatThrownBy(() -> whenSave(model)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Combination not found"); + } + + @Test + void shouldReturnAttemptsWhenFindAllByPrenda() { + givenAttemptsByPrenda(1L, 2L, 10L); + + var result = whenFindAllByPrenda(10L); + + thenResultShouldNotBeEmpty(result); + } + + @Test + void shouldReturnAttemptsFromLastNDays() { + givenAttemptsFromLastDays(); + + var result = whenFindLastNDays(5); + + thenResultShouldNotBeEmpty(result); + } + + @Test + void shouldReturnAllAttempts() { + givenAllAttempts(); + + var result = whenFindAll(); + + thenResultShouldNotBeEmpty(result); + } + + @Test + void shouldCallDeleteAllAttemptsByGarmentCode() { + whenDeleteAllByGarment("PANT001"); + + verify(combinationAttemptJpaRepository).deleteAllAttemptsByGarmentCode("PANT001"); + } + + private CombinationAttemptModel givenModelWithUserAndCombinationId(Long userId, Long combId) { + UserModel user = new UserModel(userId, "test@mail.com"); + + CombinationModel combination = new CombinationModel(null, null); + combination.setId(combId); + + return new CombinationAttemptModel(user, combination, LocalDateTime.now(), "url"); + } + + private BrandModel givenMarca(String codigo) { + BrandModel marca = new BrandModel(); + marca.setCodigoMarca(codigo); + marca.setNombre("MarcaTest"); + return marca; + } + + private CombinationAttemptModel givenModelWithPrendas(Long supId, Long infId) { + UserModel user = new UserModel(1L, "user@mail.com"); + BrandModel marcaSup = givenMarca("MARCA-SUP"); + BrandModel marcaInf = givenMarca("MARCA-INF"); + + PrendaModel sup = new PrendaModel(supId, "Sup", "img", marcaSup); + PrendaModel inf = new PrendaModel(infId, "Inf", "img", marcaInf); + + CombinationModel comb = new CombinationModel(sup, inf); + + return new CombinationAttemptModel(user, comb, LocalDateTime.now(), "url"); + } + private void givenExistingUser(Long id) { + UserEntity user = new UserEntity(); + user.setId(id); + when(userJpaRepository.findById(id)).thenReturn(Optional.of(user)); + } + + private void givenUserNotFound(Long id) { + when(userJpaRepository.findById(id)).thenReturn(Optional.empty()); + } + + private void givenExistingCombination(Long id) { + CombinationEntity comb = new CombinationEntity(); + comb.setId(id); + when(combinationJpaRepository.findById(id)).thenReturn(Optional.of(comb)); + } + + private void givenCombinationIdNotFound(Long id) { + when(combinationJpaRepository.findById(id)).thenReturn(Optional.empty()); + } + + private void givenCombinationFoundByPrendas(Long supId, Long infId, Long combId) { + CombinationEntity comb = new CombinationEntity(); + comb.setId(combId); + + when(combinationJpaRepository.findByPrendas(supId, infId)) + .thenReturn(Optional.of(comb)); + } + + private void givenCombinationNotFoundByPrendas() { + when(combinationJpaRepository.findByPrendas(any(), any())).thenReturn(Optional.empty()); + } + + private void givenCreatedCombination(Long id) { + CombinationEntity comb = new CombinationEntity(); + comb.setId(id); + + when(combinationJpaRepository.save(any())).thenReturn(comb); + } + + private void givenSavedAttempt(Long id) { + CombinationAttemptEntity attempt = new CombinationAttemptEntity(); + attempt.setId(id); + when(combinationAttemptJpaRepository.save(any())).thenReturn(attempt); + } + + private void givenAttemptsByPrenda(Long supId, Long infId, Long attemptId) { + CombinationAttemptEntity attempt = minimalAttempt(attemptId); + when(combinationAttemptJpaRepository + .findByCombination_PrendaSuperior_IdOrCombination_PrendaInferior_Id(any(), any())) + .thenReturn(List.of(attempt)); + } + + private void givenAttemptsFromLastDays() { + when(combinationAttemptJpaRepository.findByCreatedAtAfter(any())) + .thenReturn(List.of(minimalAttempt(1L))); + } + + private void givenAllAttempts() { + when(combinationAttemptJpaRepository.findAll()) + .thenReturn(List.of(minimalAttempt(1L))); + } + + private Long whenSave(CombinationAttemptModel model) { + return repository.save(model); + } + + private List whenFindAllByPrenda(Long id) { + return repository.findAllByPrenda(id); + } + + private List whenFindLastNDays(int days) { + return repository.findLastNDays(days); + } + + private List whenFindAll() { + return repository.findAll(); + } + + private void whenDeleteAllByGarment(String garmentCode) { + repository.deleteAllByAttempsReltedToCombinationRelatedToGarments(garmentCode); + } + + private void thenAttemptIdShouldBe(Long actual, Long expected) { + assertThat(actual).isEqualTo(expected); + } + + private void thenResultShouldNotBeEmpty(List list) { + assertThat(list).isNotEmpty(); + } + + private CombinationAttemptEntity minimalAttempt(Long id) { + CombinationEntity combination = new CombinationEntity(); + combination.setId(10L); + + PrendaEntity sup = new PrendaEntity(); + sup.setId(1L); + sup.setNombre("Sup"); + sup.setColor(new ColorEntity("Rojo", 1)); + sup.setMarca(new MarcaEntity("ADIDAS", "Adidas", "img", "site")); + + PrendaEntity inf = new PrendaEntity(); + inf.setId(2L); + inf.setNombre("Inf"); + inf.setColor(new ColorEntity("Azul", 2)); + inf.setMarca(new MarcaEntity("NIKE", "Nike", "img", "site")); + + combination.setPrendaSuperior(sup); + combination.setPrendaInferior(inf); + + CombinationAttemptEntity attempt = new CombinationAttemptEntity(); + attempt.setId(id); + attempt.setCombination(combination); + attempt.setCreatedAt(LocalDateTime.now()); + + return attempt; + } + +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/CombinationRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/CombinationRepositoryImplTest.java new file mode 100644 index 0000000..b422d00 --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/CombinationRepositoryImplTest.java @@ -0,0 +1,164 @@ +package com.outfitlab.project.infrastructure.repositories; + +import com.outfitlab.project.domain.model.BrandModel; +import com.outfitlab.project.domain.model.CombinationModel; +import com.outfitlab.project.domain.model.PrendaModel; +import com.outfitlab.project.infrastructure.model.CombinationEntity; +import com.outfitlab.project.infrastructure.model.MarcaEntity; +import com.outfitlab.project.infrastructure.model.PrendaEntity; +import com.outfitlab.project.infrastructure.repositories.interfaces.CombinationJpaRepository; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.Optional; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; +@SpringBootTest +@ActiveProfiles("test") +class CombinationRepositoryImplTest { + @Mock + private CombinationJpaRepository jpaRepository; + + @InjectMocks + private CombinationRepositoryImpl repository; + + @Test + void shouldFindById() { + givenExistingCombination(10L); + + Optional result = whenFindById(10L); + + thenResultShouldBePresent(result); + } + + @Test + void shouldReturnEmptyWhenIdNotFound() { + givenCombinationNotFoundById(50L); + + Optional result = whenFindById(50L); + + thenResultShouldBeEmpty(result); + } + + @Test + void shouldFindByPrendas() { + givenExistingCombinationByPrendas(1L, 2L, 100L); + + Optional result = whenFindByPrendas(1L, 2L); + + thenResultShouldBePresent(result); + } + + @Test + void shouldReturnEmptyWhenPrendasNotFound() { + givenCombinationNotFoundByPrendas(); + + Optional result = whenFindByPrendas(1L, 2L); + + thenResultShouldBeEmpty(result); + } + + @Test + void shouldSave() { + CombinationModel model = givenCombinationModel(5L, 6L); + givenSavedCombination(200L); + + CombinationModel saved = whenSave(model); + + thenCombinationIdShouldBe(saved, 200L); + } + + @Test + void shouldDeleteAllByGarmentCode() { + whenDeleteAllByGarment("X123"); + + thenDeleteShouldBeCalled("X123"); + } + + private void givenExistingCombination(Long id) { + CombinationEntity entity = minimalCombinationEntity(id); + when(jpaRepository.findById(id)).thenReturn(Optional.of(entity)); + } + + private void givenCombinationNotFoundById(Long id) { + when(jpaRepository.findById(id)).thenReturn(Optional.empty()); + } + + private void givenExistingCombinationByPrendas(Long supId, Long infId, Long combId) { + CombinationEntity entity = minimalCombinationEntity(combId); + when(jpaRepository.findByPrendas(supId, infId)).thenReturn(Optional.of(entity)); + } + + private void givenCombinationNotFoundByPrendas() { + when(jpaRepository.findByPrendas(any(), any())).thenReturn(Optional.empty()); + } + + private void givenSavedCombination(Long id) { + CombinationEntity saved = minimalCombinationEntity(id); + when(jpaRepository.save(any())).thenReturn(saved); + } + + private Optional whenFindById(Long id) { + return repository.findById(id); + } + + private Optional whenFindByPrendas(Long supId, Long infId) { + return repository.findByPrendas(supId, infId); + } + + private CombinationModel whenSave(CombinationModel model) { + return repository.save(model); + } + + private void whenDeleteAllByGarment(String code) { + repository.deleteAllByGarmentcode(code); + } + + private void thenResultShouldBePresent(Optional result) { + assertThat(result).isPresent(); + } + + private void thenResultShouldBeEmpty(Optional result) { + assertThat(result).isEmpty(); + } + + private void thenCombinationIdShouldBe(CombinationModel model, Long expected) { + assertThat(model.getId()).isEqualTo(expected); + } + + private void thenDeleteShouldBeCalled(String code) { + verify(jpaRepository).deleteAllByGarmentCode(code); + } + + private CombinationEntity minimalCombinationEntity(Long id) { + PrendaEntity sup = new PrendaEntity(); + sup.setId(1L); + sup.setMarca(new MarcaEntity("A", "MarcaA")); + + PrendaEntity inf = new PrendaEntity(); + inf.setId(2L); + inf.setMarca(new MarcaEntity("B", "MarcaB")); + + CombinationEntity entity = new CombinationEntity(sup, inf); + entity.setId(id); + return entity; + } + + private BrandModel givenMarca(String code) { + BrandModel m = new BrandModel(); + m.setCodigoMarca(code); + m.setNombre("MarcaTest"); + return m; + } + + private CombinationModel givenCombinationModel(Long supId, Long infId) { + PrendaModel sup = new PrendaModel(supId, "Sup", "img", givenMarca("MS")); + PrendaModel inf = new PrendaModel(infId, "Inf", "img", givenMarca("MI")); + + return new CombinationModel(sup, inf); + } +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/FashnRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/FashnRepositoryImplTest.java new file mode 100644 index 0000000..2ccfd66 --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/FashnRepositoryImplTest.java @@ -0,0 +1,169 @@ +package com.outfitlab.project.infrastructure.repositories; + +import static org.junit.jupiter.api.Assertions.*; + +import com.outfitlab.project.domain.exceptions.FashnApiException; +import com.outfitlab.project.domain.exceptions.PredictionFailedException; +import com.outfitlab.project.domain.exceptions.PredictionTimeoutException; +import com.outfitlab.project.presentation.dto.FashnResponse; +import com.outfitlab.project.presentation.dto.StatusResponse; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.*; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.web.client.RestTemplate; + +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; +@SpringBootTest +@ActiveProfiles("test") +class FashnRepositoryImplTest { + + @Mock + private RestTemplate restTemplate; + + @InjectMocks + private FashnRepositoryImpl repository; + + @Test + void shouldReturnIdWhenCombineOk() throws Exception { + HttpEntity> expectedEntity = givenHttpEntityForCombine(); + givenFashnRunResponse("ABC123"); + + String result = whenCombine("garment.jpg", "tops", "MAN"); + + thenShouldReturn(result, "ABC123"); + } + + @Test + void shouldThrowExceptionWhenRunReturnsNonOK() throws Exception { + HttpEntity> expectedEntity = givenHttpEntityForCombine(); + givenRunReturnsStatus(HttpStatus.BAD_REQUEST); + + assertThrows(FashnApiException.class, + () -> whenCombine("img.jpg", "tops", "MAN")); + } + + @Test + void shouldReturnImageUrlWhenPollingCompletes() throws Exception { + givenStatusSequenceCompleted("https://final-image.com/result.jpg"); + + String result = whenPollStatus("XYZ999"); + + thenShouldReturn(result, "https://final-image.com/result.jpg"); + } + + @Test + void shouldThrowTimeoutWhenPollingExceedsAttempts() { + givenStatusSequenceNeverCompletes(); + + assertThrows(PredictionTimeoutException.class, + () -> whenPollStatus("XYZ999")); + } + + @Test + void shouldThrowPredictionFailedWhenStatusFailed() { + givenStatusFailed(); + + assertThrows(PredictionFailedException.class, + () -> whenPollStatus("XYZ999")); + } + + private HttpEntity> givenHttpEntityForCombine() throws Exception { + UserDetails mockUser = mock(UserDetails.class); + return new HttpEntity<>(Map.of(), new HttpHeaders()); + } + + private void givenFashnRunResponse(String id) { + FashnResponse resp = new FashnResponse(); + resp.setId(id); + + ResponseEntity entity = + new ResponseEntity<>(resp, HttpStatus.OK); + + when(restTemplate.exchange( + contains("/run"), + eq(HttpMethod.POST), + any(HttpEntity.class), + eq(FashnResponse.class) + )).thenReturn(entity); + } + + private void givenRunReturnsStatus(HttpStatus status) { + ResponseEntity entity = + new ResponseEntity<>(null, status); + + when(restTemplate.exchange( + contains("/run"), + eq(HttpMethod.POST), + any(HttpEntity.class), + eq(FashnResponse.class) + )).thenReturn(entity); + } + + private void givenStatusSequenceCompleted(String finalUrl) { + StatusResponse first = new StatusResponse("XYZ999", "processing", null, null); + StatusResponse second = new StatusResponse("XYZ999", "completed", List.of(finalUrl), null); + + when(restTemplate.exchange( + contains("/status"), + eq(HttpMethod.GET), + any(HttpEntity.class), + eq(StatusResponse.class), + anyString() + )).thenReturn( + new ResponseEntity<>(first, HttpStatus.OK), + new ResponseEntity<>(second, HttpStatus.OK) + ); + } + + private void givenStatusSequenceNeverCompletes() { + StatusResponse ongoing = new StatusResponse("ABC123","processing", null, null); + + ResponseEntity res = + new ResponseEntity<>(ongoing, HttpStatus.OK); + + when(restTemplate.exchange( + contains("/status"), + eq(HttpMethod.GET), + any(HttpEntity.class), + eq(StatusResponse.class), + anyString() + )).thenReturn(res); + } + + private void givenStatusFailed() { + StatusResponse failed = new StatusResponse("ABC123", "failed", null, null); + + ResponseEntity entity = + new ResponseEntity<>(failed, HttpStatus.OK); + + when(restTemplate.exchange( + contains("/status"), + eq(HttpMethod.GET), + any(HttpEntity.class), + eq(StatusResponse.class), + anyString() + )).thenReturn(entity); + } + + private String whenCombine(String garment, String category, String avatar) throws Exception { + UserDetails mockUser = mock(UserDetails.class); + return repository.combine(garment, category, avatar, mockUser); + } + + private String whenPollStatus(String id) throws Exception { + return repository.pollStatus(id); + } + + private void thenShouldReturn(String actual, String expected) { + assertThat(actual).isEqualTo(expected); + } + +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/GarmentRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/GarmentRepositoryImplTest.java new file mode 100644 index 0000000..2fa3578 --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/GarmentRepositoryImplTest.java @@ -0,0 +1,218 @@ +package com.outfitlab.project.infrastructure.repositories; + +import com.outfitlab.project.domain.exceptions.GarmentNotFoundException; +import com.outfitlab.project.domain.model.PrendaModel; +import com.outfitlab.project.domain.model.dto.PageDTO; +import com.outfitlab.project.infrastructure.model.*; +import com.outfitlab.project.infrastructure.repositories.interfaces.*; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Optional; + +import static org.mockito.Mockito.verify; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +@SpringBootTest +@ActiveProfiles("test") +class GarmentRepositoryImplTest { + @Mock + private GarmentJpaRepository garmentJpaRepository; + @Mock + private BrandJpaRepository brandJpaRepository; + @Mock + private ColorJpaRepository colorJpaRepository; + @Mock + private ClimaJpaRepository climaJpaRepository; + @Mock + private OcasionJpaRepository ocasionJpaRepository; + + @InjectMocks + private GarmentRepositoryImpl repository; + + @Test + void shouldFindByBrandCodeAndTipo() { + givenGarmentsPage("brand1", "superior", 0); + + PageDTO result = whenFindByBrandCodeAndTipo("brand1", "superior", 0); + + thenPageDTOShouldBeCorrect(result, 1); + } + + @Test + void shouldThrowWhenGarmentCodeNotFound() { + givenGarmentNotFound("G123"); + + assertThatThrownBy(() -> whenFindByGarmentCode("G123")) + .isInstanceOf(GarmentNotFoundException.class) + .hasMessageContaining("No encontramos la prenda"); + } + + @Test + void shouldFindGarmentByGarmentCode() { + givenExistingGarment("G123"); + + PrendaModel model = whenFindByGarmentCode("G123"); + + thenGarmentCodeShouldBe(model, "G123"); + } + + @Test + void shouldCreateGarmentSuccessfully() { + givenBrandExists("B1"); + givenColorExists("Red"); + givenClimaExists("Frio"); + givenOcasionExists("Casual"); + + whenCreateGarment("Prenda1", "G123", "superior", "Red", "B1", "img.png", "Frio", + List.of("Casual"), "Hombre"); + + thenGarmentShouldBeSaved(); + } + + @Test + void shouldUpdateGarmentSuccessfully() { + givenExistingGarmentEntity("G123"); + givenColorExists("Blue"); + givenClimaExists("Calor"); + givenOcasionExists("Formal"); + + whenUpdateGarment("Prenda2", "inferior", "Blue", "Formal", "G123", "newImg.png", "G124", "Calor", + List.of("Formal"), "Mujer"); + + thenGarmentShouldBeSaved(); + } + + @Test + void shouldDeleteGarmentSuccessfully() { + String code = "G123"; + + whenDeleteGarment(code); + + thenDeleteByGarmentCodeShouldBeCalled(code); + } + + private void givenGarmentsPage(String brandCode, String tipo, int page) { + // Marca + MarcaEntity brand = new MarcaEntity(); + brand.setCodigoMarca(brandCode); + brand.setNombre("Brand Name"); + + // Color + ColorEntity color = new ColorEntity(); + color.setNombre("Rojo"); + color.setValor(1); + + // Clima + ClimaEntity clima = new ClimaEntity(); + clima.setNombre("Frio"); + + // Prenda + PrendaEntity entity = new PrendaEntity(); + entity.setGarmentCode("G1"); + entity.setNombre("Prenda Test"); + entity.setMarca(brand); + entity.setColor(color); + entity.setClimaAdecuado(clima); + entity.setTipo(tipo); + + Page pageResult = new PageImpl<>(List.of(entity), PageRequest.of(page, 10), 1); + + when(garmentJpaRepository.findByMarca_CodigoMarcaAndTipo( + eq(brandCode), + eq(tipo.toLowerCase()), + any(PageRequest.class) + )).thenReturn(pageResult); + } + + private void givenGarmentNotFound(String garmentCode) { + when(garmentJpaRepository.findByGarmentCode(garmentCode)).thenReturn(null); + } + + private void givenExistingGarment(String garmentCode) { + PrendaEntity entity = new PrendaEntity(); + entity.setGarmentCode(garmentCode); + when(garmentJpaRepository.findByGarmentCode(garmentCode)).thenReturn(entity); + } + + private void givenExistingGarmentEntity(String garmentCode) { + PrendaEntity entity = new PrendaEntity(); + entity.setGarmentCode(garmentCode); + when(garmentJpaRepository.findByGarmentCode(garmentCode)).thenReturn(entity); + } + + private void givenBrandExists(String brandCode) { + MarcaEntity brand = new MarcaEntity(brandCode, "BrandName"); + when(brandJpaRepository.findByCodigoMarca(brandCode)).thenReturn(brand); + } + + private void givenColorExists(String colorNombre) { + ColorEntity color = new ColorEntity(); + color.setNombre(colorNombre); + when(colorJpaRepository.findColorEntityByNombre(colorNombre)).thenReturn(Optional.of(color)); + } + + private void givenClimaExists(String climaNombre) { + ClimaEntity clima = new ClimaEntity(); + clima.setNombre(climaNombre); + when(climaJpaRepository.findClimaEntityByNombre(climaNombre)).thenReturn(Optional.of(clima)); + } + + private void givenOcasionExists(String ocasionNombre) { + OcasionEntity ocasion = new OcasionEntity(); + ocasion.setNombre(ocasionNombre); + when(ocasionJpaRepository.findOcasionEntityByNombre(ocasionNombre)).thenReturn(Optional.of(ocasion)); + } + + private PageDTO whenFindByBrandCodeAndTipo(String brandCode, String tipo, int page) { + return repository.findByBrandCodeAndTipo(brandCode, tipo, page); + } + + private PrendaModel whenFindByGarmentCode(String code) { + return repository.findByGarmentCode(code); + } + + private void whenCreateGarment(String name, String garmentCode, String type, String colorNombre, + String brandCode, String imageUrl, String climaNombre, List ocasiones, String genero) { + repository.createGarment(name, garmentCode, type, colorNombre, brandCode, imageUrl, climaNombre, ocasiones, genero); + } + + private void whenUpdateGarment(String name, String type, String colorNombre, String event, String garmentCode, + String imageUrl, String newGarmentCode, String climaNombre, List ocasiones, String genero) { + repository.updateGarment(name, type, colorNombre, event, garmentCode, imageUrl, newGarmentCode, climaNombre, ocasiones, genero); + } + + private void whenDeleteGarment(String garmentCode) { + repository.deleteGarment(garmentCode); + } + + private void thenPageDTOShouldBeCorrect(PageDTO dto, int expectedSize) { + assertThat(dto.getContent()).hasSize(expectedSize); + assertThat(dto.getPage()).isZero(); + assertThat(dto.getSize()).isEqualTo(10); + assertThat(dto.getTotalElements()).isEqualTo(1); + } + + private void thenGarmentCodeShouldBe(PrendaModel model, String code) { + assertThat(model.getGarmentCode()).isEqualTo(code); + } + + private void thenGarmentShouldBeSaved() { + verify(garmentJpaRepository).save(any(PrendaEntity.class)); + } + + private void thenDeleteByGarmentCodeShouldBeCalled(String code) { + verify(garmentJpaRepository).deleteByGarmentCode(code); + } + + +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/OcasionRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/OcasionRepositoryImplTest.java new file mode 100644 index 0000000..bfa9b86 --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/OcasionRepositoryImplTest.java @@ -0,0 +1,67 @@ +package com.outfitlab.project.infrastructure.repositories; + +import com.outfitlab.project.domain.exceptions.OcasionNotFoundException; +import com.outfitlab.project.domain.model.OcasionModel; +import com.outfitlab.project.infrastructure.model.OcasionEntity; +import com.outfitlab.project.infrastructure.repositories.interfaces.OcasionJpaRepository; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.when; + +@SpringBootTest +@ActiveProfiles("test") +class OcasionRepositoryImplTest { + + @Mock + private OcasionJpaRepository ocasionJpaRepository; + + @InjectMocks + private OcasionRepositoryImpl repository; + + @Test + void shouldReturnAllOcasiones() { + givenExistingOcaciones(1); + + List result = whenFindAll(); + + thenListSizeShouldBe(result, 1); + } + + @Test + void shouldThrowExceptionWhenNoOcasionesFound() { + givenNoOcaciones(); + + assertThrows(OcasionNotFoundException.class, this::whenFindAll); + } + + private void givenExistingOcaciones(int qty) { + List entities = new ArrayList<>(); + for (int i = 0; i < qty; i++) { + OcasionEntity e = new OcasionEntity(); + e.setNombre("Nombre " + i); + entities.add(e); + } + when(ocasionJpaRepository.findAll()).thenReturn(entities); + } + + private void givenNoOcaciones() { + when(ocasionJpaRepository.findAll()).thenReturn(List.of()); + } + + private List whenFindAll() { + return repository.findAllOcasiones(); + } + + private void thenListSizeShouldBe(List list, int expected) { + assertEquals(expected, list.size()); + } +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/PrendaOcasionRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/PrendaOcasionRepositoryImplTest.java new file mode 100644 index 0000000..c4a8cbc --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/PrendaOcasionRepositoryImplTest.java @@ -0,0 +1,41 @@ +package com.outfitlab.project.infrastructure.repositories; +import com.outfitlab.project.infrastructure.repositories.interfaces.PrendaOcasionJpaRepository; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import static org.mockito.Mockito.verify; + +@SpringBootTest +@ActiveProfiles("test") +class PrendaOcasionRepositoryImplTest { + + @Mock + private PrendaOcasionJpaRepository prendaOcasionJpaRepository; + + @InjectMocks + private PrendaOcasionRepositoryImpl repository; + + @Test + public void shouldDeleteAllPrendaOcacionByGarment() { + String garmentCode = givenGarmentCode(); + + whenDeleteByGarmentCode(garmentCode); + + thenRepositoryShouldCallDeleteMethod(garmentCode); + } + + private String givenGarmentCode() { + return "GARM123"; + } + + private void whenDeleteByGarmentCode(String garmentCode) { + repository.deleteAllPrendaOcasionByGarment(garmentCode); + } + + private void thenRepositoryShouldCallDeleteMethod(String garmentCode) { + verify(prendaOcasionJpaRepository).deleteAllByGarmentCode(garmentCode); + } +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/RecomendationRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/RecomendationRepositoryImplTest.java new file mode 100644 index 0000000..9ca8acd --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/RecomendationRepositoryImplTest.java @@ -0,0 +1,183 @@ +package com.outfitlab.project.infrastructure.repositories; + + +import com.outfitlab.project.domain.exceptions.GarmentNotFoundException; +import com.outfitlab.project.domain.model.GarmentRecomendationModel; +import com.outfitlab.project.infrastructure.model.GarmentRecomendationEntity; +import com.outfitlab.project.infrastructure.model.PrendaEntity; +import com.outfitlab.project.infrastructure.repositories.interfaces.GarmentJpaRepository; +import com.outfitlab.project.infrastructure.repositories.interfaces.RecomendationJpaRepository; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; +@SpringBootTest +@ActiveProfiles("test") +class RecomendationRepositoryImplTest { + @Mock + private RecomendationJpaRepository recomendationJpaRepository; + + @Mock + private GarmentJpaRepository garmentJpaRepository; + + @InjectMocks + private RecomendationRepositoryImpl repository; + + @Test + void shouldFindRecomendationsByGarmentCode() throws GarmentNotFoundException { + PrendaEntity garment = givenPrendaEntity("G1", "SUPERIOR"); + List entities = givenRecomendationEntities(garment, 2); + givenFindByTopGarment(garment, entities); + + List result = whenFindRecomendations("G1"); + + thenResultShouldHaveSize(result, 2); + } + + @Test + void shouldThrowWhenGarmentNotFound() { + givenGarmentNotFound("INVALID"); + + assertThatThrownBy(() -> whenFindRecomendations("INVALID")) + .isInstanceOf(GarmentNotFoundException.class) + .hasMessageContaining("No encontramos la prenda con el código"); + } + + @Test + void shouldDeleteRecomendationsByGarmentCode() { + whenDeleteRecomendations("G001"); + + thenDeleteShouldBeCalled("G001"); + } + + @Test + void shouldCreateSugerenciasByGarmentCode() throws GarmentNotFoundException { + PrendaEntity principal = givenPrendaEntity("G001", "SUPERIOR"); + List sugerencias = List.of("G002", "G003"); + givenGarmentsExist(sugerencias); + givenSavedRecomendations(); + + whenCreateSugerencias("G001", "inferior", sugerencias); + + thenSaveAllShouldBeCalled(); + } + + @Test + void shouldThrowWhenInvalidTypeInCreateSugerencias() { + PrendaEntity principal = givenPrendaEntity("G001", "SUPERIOR"); + List sugerencias = List.of("G002"); + + givenGarmentsExist(sugerencias); + + assertThatThrownBy(() -> whenCreateSugerencias("G001", "INVALID", sugerencias)) + .isInstanceOf(GarmentNotFoundException.class) + .hasMessageContaining("Tipo de prenda inválido"); + } + + @Test + void shouldDeleteRecomendationByGarmentsCodeWhenTypeIsInferior() { + whenDeleteRecomendation("G001", "G002", "inferior"); + + thenDeleteWhenPrimaryIsBottomCalled("G001", "G002"); + } + + @Test + void shouldDeleteRecomendationByGarmentsCodeWhenTypeIsSuperior() { + whenDeleteRecomendation("G001", "G002", "superior"); + + thenDeleteWhenPrimaryIsTopCalled("G001", "G002"); + } + + private PrendaEntity givenPrendaEntity(String code, String tipo) { + PrendaEntity p = new PrendaEntity(); + p.setGarmentCode(code); + p.setTipo(tipo); + when(garmentJpaRepository.findByGarmentCode(code)).thenReturn(p); + return p; + } + + private void givenGarmentNotFound(String code) { + when(garmentJpaRepository.findByGarmentCode(code)).thenReturn(null); + } + + private List givenRecomendationEntities(PrendaEntity garment, int count) { + List list = new ArrayList<>(); + for (int i = 0; i < count; i++) { + GarmentRecomendationEntity entity = new GarmentRecomendationEntity(); + + // Asumimos que si la prenda principal es SUPERIOR, ponemos la inferior ficticia + PrendaEntity other = new PrendaEntity(); + other.setGarmentCode("DUMMY" + i); + other.setTipo(garment.getTipo().equalsIgnoreCase("SUPERIOR") ? "INFERIOR" : "SUPERIOR"); + other.setMarca(new com.outfitlab.project.infrastructure.model.MarcaEntity("X"+i, "MarcaDummy"+i)); + + if (garment.getTipo().equalsIgnoreCase("SUPERIOR")) { + entity.setTopGarment(garment); + entity.setBottomGarment(other); + } else { + entity.setBottomGarment(garment); + entity.setTopGarment(other); + } + + list.add(entity); + } + return list; + } + + private void givenFindByTopGarment(PrendaEntity garment, List result) { + when(recomendationJpaRepository.findByTopGarment(garment)).thenReturn(result); + } + + private void givenGarmentsExist(List codes) { + for (String code : codes) { + givenPrendaEntity(code, "INFERIOR"); + } + } + + private void givenSavedRecomendations() { + when(recomendationJpaRepository.saveAll(anyList())).thenReturn(new ArrayList<>()); + } + + private List whenFindRecomendations(String code) throws GarmentNotFoundException { + return repository.findRecomendationsByGarmentCode(code); + } + + private void whenDeleteRecomendations(String code) { + repository.deleteRecomendationsByGarmentCode(code); + } + + private void whenCreateSugerencias(String code, String type, List sugerencias) throws GarmentNotFoundException { + repository.createSugerenciasByGarmentCode(code, type, sugerencias); + } + + private void whenDeleteRecomendation(String primary, String secondary, String type) { + repository.deleteRecomendationByGarmentsCode(primary, secondary, type); + } + + private void thenResultShouldHaveSize(List list, int size) { + assertThat(list).hasSize(size); + } + + private void thenDeleteShouldBeCalled(String code) { + verify(recomendationJpaRepository).deleteAllByGarmentCode(code); + } + + private void thenSaveAllShouldBeCalled() { + verify(recomendationJpaRepository).saveAll(anyList()); + } + + private void thenDeleteWhenPrimaryIsBottomCalled(String primary, String secondary) { + verify(recomendationJpaRepository).deleteWhenPrimaryIsBottom(primary, secondary); + } + + private void thenDeleteWhenPrimaryIsTopCalled(String primary, String secondary) { + verify(recomendationJpaRepository).deleteWhenPrimaryIsTop(primary, secondary); + } +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/SubscriptionRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/SubscriptionRepositoryImplTest.java new file mode 100644 index 0000000..4d15deb --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/SubscriptionRepositoryImplTest.java @@ -0,0 +1,134 @@ +package com.outfitlab.project.infrastructure.repositories; + +import com.outfitlab.project.domain.model.SubscriptionModel; +import com.outfitlab.project.infrastructure.model.SubscriptionEntity; +import com.outfitlab.project.infrastructure.repositories.interfaces.SubscriptionJpaRepository; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; + +@SpringBootTest +@ActiveProfiles("test") +class SubscriptionRepositoryImplTest { + @Mock + private SubscriptionJpaRepository jpaRepository; + + @InjectMocks + private SubscriptionRepositoryImpl repository; + + @Test + public void shouldGetAllSubscriptions() { + // given + List entities = givenSubscriptionEntities(); + givenJpaFindAllReturns(entities); + + // when + List result = whenGetAllSubscriptions(); + + // then + thenResultShouldMatchEntities(result, entities); + } + + @Test + public void shouldGetSubscriptionByPlanCode() { + // given + String planCode = "PREMIUM"; + SubscriptionEntity entity = givenSubscriptionEntity(planCode); + givenJpaFindByPlanCodeReturns(planCode, entity); + + // when + SubscriptionModel result = whenGetByPlanCode(planCode); + + // then + thenModelShouldMatchEntity(result, entity); + } + + @Test + public void shouldThrowExceptionWhenPlanCodeNotFound() { + // given + String planCode = "INVALID"; + givenJpaFindByPlanCodeReturnsEmpty(planCode); + + // then + thenShouldThrowPlanNotFound(planCode); + } + @Test + public void shouldFindSubscriptionsByPlanType() { + // given + String planType = "MONTHLY"; + List entities = givenSubscriptionEntities(); + givenJpaFindByPlanTypeReturns(planType, entities); + + List result = whenFindByPlanType(planType); + + thenResultShouldMatchEntities(result, entities); + } + + private List givenSubscriptionEntities() { + return List.of( + new SubscriptionEntity("ESTANDAR", "ABC123"), + new SubscriptionEntity("PRO", "DEF456") + ); + } + + private SubscriptionEntity givenSubscriptionEntity(String planCode) { + return new SubscriptionEntity(planCode, "ABC123"); + } + + private void givenJpaFindAllReturns(List entities) { + when(jpaRepository.findAll()).thenReturn(entities); + } + + private void givenJpaFindByPlanCodeReturns(String planCode, SubscriptionEntity entity) { + when(jpaRepository.findByPlanCode(planCode)).thenReturn(Optional.of(entity)); + } + + private void givenJpaFindByPlanCodeReturnsEmpty(String planCode) { + when(jpaRepository.findByPlanCode(planCode)).thenReturn(Optional.empty()); + } + + private void givenJpaFindByPlanTypeReturns(String planType, List entities) { + when(jpaRepository.findByPlanType(planType)).thenReturn(entities); + } + + private List whenGetAllSubscriptions() { + return repository.getAllSubscriptions(); + } + + private SubscriptionModel whenGetByPlanCode(String planCode) { + return repository.getByPlanCode(planCode); + } + + private List whenFindByPlanType(String planType) { + return repository.findByPlanType(planType); + } + private void thenShouldThrowPlanNotFound(String planCode) { + assertThatThrownBy(() -> whenGetByPlanCode(planCode)) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Plan no encontrado: " + planCode); + } + + private void thenResultShouldMatchEntities(List result, + List entities) { + assertEquals(entities.size(), result.size()); + + for (int i = 0; i < entities.size(); i++) { + thenModelShouldMatchEntity(result.get(i), entities.get(i)); + } + } + + private void thenModelShouldMatchEntity(SubscriptionModel model, SubscriptionEntity entity) { + assertEquals(entity.getPlanCode(), model.getPlanCode()); + assertEquals(entity.getPlanType(), model.getPlanType()); + assertEquals(entity.getPrice(), model.getPrice()); + } +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/TripoRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/TripoRepositoryImplTest.java new file mode 100644 index 0000000..a69aafd --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/TripoRepositoryImplTest.java @@ -0,0 +1,182 @@ +package com.outfitlab.project.infrastructure.repositories; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.outfitlab.project.domain.model.TripoModel; +import com.outfitlab.project.infrastructure.model.TripoEntity; +import com.outfitlab.project.infrastructure.repositories.interfaces.TripoJpaRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; + +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.*; + +import static org.mockito.Mockito.*; + +@SpringBootTest +@ActiveProfiles("test") +class TripoRepositoryImplTest { + + @Mock + private ObjectMapper mapper; + @Mock + private RestTemplate restTemplate; + @Mock + private TripoJpaRepository tripoJpaRepository; + + @Spy + @InjectMocks + private TripoRepositoryImpl repository; + + @BeforeEach + void setUp() { + ReflectionTestUtils.setField(repository, "tripoApiKey", "test-api-key"); + } + + private MultipartFile mockMultipartFile(String name) { + return new MockMultipartFile(name, name, "image/png", "dummy".getBytes()); + } + + private ResponseEntity mockResponse(String body) { + return ResponseEntity.ok(body); + } + + @Test + void shouldReturnTripoModelWhenFindByTaskId() { + TripoEntity entity = givenTripoEntity("task123"); + TripoModel result = whenBuscarPorTaskId("task123"); + + thenTripoModelShouldHaveTaskId(result, "task123"); + } + + @Test + void shouldReturnTaskIdWhenRequestGenerateGlbToTripo() throws Exception { + Map uploadData = new HashMap<>(); + uploadData.put("imageToken", "token123"); + uploadData.put("fileExtension", "png"); + + ResponseEntity response = givenTaskResponse("{\"data\":{\"task_id\":\"task123\"}}"); + + String taskId = whenRequestGenerateGlbToTripo(uploadData, response); + + thenTaskIdShouldBe(taskId, "task123"); + } + + @Test + void shouldSaveTripoModel() { + TripoModel model = givenTripoModel("task123"); + TripoEntity entity = TripoEntity.convertToEntity(model); + doReturn(entity).when(tripoJpaRepository).save(any(TripoEntity.class)); + + TripoModel result = whenSaveTripoModel(model); + + thenTripoModelShouldHaveTaskId(result, "task123"); + } + + @Test + void shouldUpdateTripoModel() throws Exception { + TripoModel model = givenTripoModel("task123"); + TripoEntity entity = TripoEntity.convertToEntity(model); + doReturn(entity).when(tripoJpaRepository).findByTaskId("task123"); + doReturn(entity).when(tripoJpaRepository).save(any(TripoEntity.class)); + + TripoModel result = whenUpdateTripoModel(model); + + thenTripoModelShouldHaveTaskId(result, "task123"); + } + + @Test + void shouldReturnGlbUrlWhenStatusIsSuccess() throws Exception { + String taskId = "task123"; + String jsonResponse = "{\"data\":{\"status\":\"success\",\"result\":{\"pbr_model\":{\"url\":\"https://glb.com/model.glb\"}}}}"; + ResponseEntity response = new ResponseEntity<>(jsonResponse, HttpStatus.OK); + + doReturn(response).when(repository).requestTripoTaskStatus(eq(taskId), any(HttpEntity.class)); + + String result = whenRequestStatusGlbTripo(taskId); + + thenGlbUrlShouldBe(result, "https://glb.com/model.glb"); + } + + private TripoModel whenUpdateTripoModel(TripoModel model) throws Exception { + return repository.update(model); + } + + private TripoEntity givenTripoEntity(String taskId) { + TripoEntity entity = new TripoEntity(); + entity.setTaskId(taskId); + doReturn(entity).when(tripoJpaRepository).findByTaskId(taskId); + return entity; + } + + private TripoModel whenBuscarPorTaskId(String taskId) { + return repository.buscarPorTaskId(taskId); + } + + private TripoModel givenTripoModel(String taskId) { + TripoModel model = new TripoModel(); + model.setTaskId(taskId); + return model; + } + + private TripoModel whenSaveTripoModel(TripoModel model) { + return repository.save(model); + } + + private void thenTripoModelShouldHaveTaskId(TripoModel model, String expectedTaskId) { + assertThat(model.getTaskId()).isEqualTo(expectedTaskId); + } + + private ResponseEntity givenUploadResponse(String body) { + doReturn(new ResponseEntity<>(body, HttpStatus.OK)) + .when(repository) + .generateRequestToUploadImageToTripo(any(MultipartFile.class), anyString()); + return new ResponseEntity<>(body, HttpStatus.OK); + } + + private Map whenRequestUploadImagenToTripo(String url, ResponseEntity response) throws Exception { + return repository.requestUploadImagenToTripo(url); + } + + private void thenUploadDataShouldContain(Map data, String token, String filename, String extension) { + assertThat(data.get("imageToken")).isEqualTo(token); + assertThat(data.get("originalFilename")).isEqualTo(filename); + assertThat(data.get("fileExtension")).isEqualTo(extension); + } + + private ResponseEntity givenTaskResponse(String body) { + ResponseEntity response = new ResponseEntity<>(body, HttpStatus.OK); + doReturn(response).when(restTemplate).postForEntity(anyString(), any(HttpEntity.class), eq(String.class)); + return response; + } + + private String whenRequestGenerateGlbToTripo(Map uploadData, ResponseEntity response) throws Exception { + return repository.requestGenerateGlbToTripo(uploadData); + } + + private void thenTaskIdShouldBe(String actual, String expected) { + assertThat(actual).isEqualTo(expected); + } + + private String whenRequestStatusGlbTripo(String taskId) throws Exception { + return repository.requestStatusGlbTripo(taskId); + } + + private void thenGlbUrlShouldBe(String actual, String expected) { + assertThat(actual).isEqualTo(expected); + } +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/UploadImageRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/UploadImageRepositoryImplTest.java new file mode 100644 index 0000000..53f4154 --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/UploadImageRepositoryImplTest.java @@ -0,0 +1,109 @@ +package com.outfitlab.project.infrastructure.repositories; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.ActiveProfiles; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.DeleteObjectRequest; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.verify; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +@SpringBootTest +@ActiveProfiles("test") +class UploadImageRepositoryImplTest { + + @Mock + private S3Client s3Client; + + private UploadImageRepositoryImpl repository; + + @BeforeEach + void setUp() { + repository = new UploadImageRepositoryImpl(s3Client, "test-bucket", "sa-east-1"); + } + + @Test + void shouldUploadFileSuccessfully() throws Exception { + MockMultipartFile file = givenMultipartFile("test.txt", "Hello world".getBytes()); + + String url = whenUploadFile(file, "folder"); + + thenPutObjectShouldBeCalled(); + thenUrlShouldBeCorrect(url, "folder"); + } + + @Test + void shouldThrowWhenUploadFails() throws Exception { + MockMultipartFile file = givenMultipartFile("fail.txt", "data".getBytes()); + doThrow(new RuntimeException("AWS error")) + .when(s3Client).putObject(any(PutObjectRequest.class), any(RequestBody.class)); + + assertThatThrownBy(() -> whenUploadFile(file, "folder")) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Error al subir archivo a S3"); + } + + @Test + void shouldDeleteFileSuccessfully() { + String key = "folder/file.txt"; + String url = repository.getFileUrl(key); + + whenDeleteFile(url); + + thenDeleteObjectShouldBeCalled(key); + } + + @Test + void shouldThrowWhenDeleteFails() { + doThrow(new RuntimeException("AWS delete error")) + .when(s3Client).deleteObject(any(DeleteObjectRequest.class)); + + assertThatThrownBy(() -> repository.deleteFile(repository.getFileUrl("fail.txt"))) + .isInstanceOf(RuntimeException.class) + .hasMessageContaining("Error al eliminar archivo de S3"); + } + + @Test + void shouldGetFileUrlCorrectly() { + String key = "folder/file.txt"; + + String url = repository.getFileUrl(key); + + assertThat(url) + .isEqualTo("https://sa-east-1.s3.test-bucket.amazonaws.com/folder/file.txt"); + } + + private MockMultipartFile givenMultipartFile(String filename, byte[] content) { + return new MockMultipartFile("file", filename, "text/plain", content); + } + + private String whenUploadFile(MockMultipartFile file, String folder) { + return repository.uploadFile(file, folder); + } + + private void whenDeleteFile(String url) { + repository.deleteFile(url); + } + + private void thenPutObjectShouldBeCalled() { + verify(s3Client, times(1)) + .putObject(any(PutObjectRequest.class), any(RequestBody.class)); + } + + private void thenUrlShouldBeCorrect(String url, String folder) { + assertThat(url).startsWith("https://sa-east-1.s3.test-bucket.amazonaws.com/" + folder + "/"); + assertThat(url).endsWith(".txt"); + } + + private void thenDeleteObjectShouldBeCalled(String key) { + verify(s3Client).deleteObject(argThat((DeleteObjectRequest req) -> req.key().equals(key))); + } +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/UserCombinationFavoriteRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/UserCombinationFavoriteRepositoryImplTest.java new file mode 100644 index 0000000..e3e579d --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/UserCombinationFavoriteRepositoryImplTest.java @@ -0,0 +1,193 @@ +package com.outfitlab.project.infrastructure.repositories; + +import com.outfitlab.project.domain.exceptions.FavoritesException; +import com.outfitlab.project.domain.exceptions.UserCombinationFavoriteNotFoundException; +import com.outfitlab.project.domain.exceptions.UserNotFoundException; +import com.outfitlab.project.domain.model.UserCombinationFavoriteModel; +import com.outfitlab.project.domain.model.dto.PageDTO; +import com.outfitlab.project.infrastructure.model.UserCombinationFavoriteEntity; +import com.outfitlab.project.infrastructure.model.UserEntity; +import com.outfitlab.project.infrastructure.repositories.interfaces.UserCombinationFavoriteJpaRepository; +import com.outfitlab.project.infrastructure.repositories.interfaces.UserJpaRepository; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +import static org.mockito.Mockito.*; + +@SpringBootTest +@ActiveProfiles("test") +class UserCombinationFavoriteRepositoryImplTest { + @Mock + private UserCombinationFavoriteJpaRepository favoriteJpaRepository; + + @Mock + private UserJpaRepository userJpaRepository; + + @InjectMocks + private UserCombinationFavoriteRepositoryImpl repository; + + private static final String EMAIL = "test@mail.com"; + private static final String URL = "https://combination.com/1"; + + @Test + void shouldReturnFavoriteWhenExists() throws Exception { + UserCombinationFavoriteEntity entity = givenFavoriteEntity(); + givenFavoriteExists(URL, EMAIL, entity); + + UserCombinationFavoriteModel result = whenFindByCombinationAndEmail(); + + thenFavoriteShouldMatch(result, URL); + } + + @Test + void shouldThrowExceptionWhenFavoriteDoesNotExist() { + givenFavoriteDoesNotExist(URL, EMAIL); + + assertThatThrownBy(() -> whenFindByCombinationAndEmail()) + .isInstanceOf(UserCombinationFavoriteNotFoundException.class) + .hasMessageContaining(URL); + } + + @Test + void shouldAddToFavoritesWhenUserExists() throws Exception { + UserEntity user = givenUser(); + givenUserExists(user); + UserCombinationFavoriteEntity saved = givenSavedFavorite(user); + + givenFavoriteSaved(saved); + + UserCombinationFavoriteEntity result = whenAddToFavorites(); + + assertThat(result.getCombinationUrl()).isEqualTo(URL); + } + + @Test + void shouldThrowExceptionWhenAddingFavoriteForNonExistingUser() { + givenUserDoesNotExist(); + + assertThatThrownBy(() -> whenAddToFavorites()) + .isInstanceOf(UserNotFoundException.class) + .hasMessageContaining(EMAIL); + } + + @Test + void shouldDeleteFavoriteWhenUserExists() throws Exception { + UserEntity user = givenUser(); + givenUserExists(user); + + UserCombinationFavoriteEntity favorite = givenFavoriteEntity(); + givenFavoriteExists(URL, EMAIL, favorite); + + whenDeleteFavorite(); + + verify(favoriteJpaRepository).delete(favorite); + } + + @Test + void shouldThrowExceptionWhenDeletingFavoriteWithNonExistingUser() { + givenUserDoesNotExist(); + + assertThatThrownBy(this::whenDeleteFavorite) + .isInstanceOf(UserNotFoundException.class); + } + + @Test + void shouldReturnPageDTOWhenUserHasFavorites() throws Exception { + UserEntity user = givenUser(); + givenUserExists(user); + + Page page = givenFavoritePage(List.of(givenFavoriteEntity())); + + givenFavoritePageExists(page); + + PageDTO result = whenGetFavoritesPage(); + + assertThat(result.getContent()).hasSize(1); + } + + @Test + void shouldThrowExceptionWhenUserHasNoFavorites() throws Exception { + UserEntity user = givenUser(); + givenUserExists(user); + + givenFavoritePageExists(givenFavoritePage(List.of())); // empty page + + assertThatThrownBy(this::whenGetFavoritesPage) + .isInstanceOf(FavoritesException.class); + } + + private UserCombinationFavoriteEntity givenFavoriteEntity() { + return new UserCombinationFavoriteEntity(new UserEntity(), URL); + } + + private UserCombinationFavoriteEntity givenSavedFavorite(UserEntity user) { + return new UserCombinationFavoriteEntity(user, URL); + } + + private UserEntity givenUser() { + UserEntity user = new UserEntity(); + user.setEmail(EMAIL); + return user; + } + + private void givenUserExists(UserEntity user) { + when(userJpaRepository.findByEmail(EMAIL)).thenReturn(user); + } + + private void givenUserDoesNotExist() { + when(userJpaRepository.findByEmail(EMAIL)).thenReturn(null); + } + + private void givenFavoriteExists(String url, String email, UserCombinationFavoriteEntity entity) { + when(favoriteJpaRepository.findBycombinationUrlAndUser_Email(url, email)) + .thenReturn(entity); + } + + private void givenFavoriteDoesNotExist(String url, String email) { + when(favoriteJpaRepository.findBycombinationUrlAndUser_Email(url, email)) + .thenReturn(null); + } + + private void givenFavoriteSaved(UserCombinationFavoriteEntity saved) { + when(favoriteJpaRepository.save(any())).thenReturn(saved); + } + + private Page givenFavoritePage(List list) { + return new PageImpl<>(list, PageRequest.of(0, 10), list.size()); + } + + private void givenFavoritePageExists(Page page) { + when(favoriteJpaRepository.findByUser_Email(eq(EMAIL), any(PageRequest.class))) + .thenReturn(page); + } + + private UserCombinationFavoriteModel whenFindByCombinationAndEmail() throws Exception { + return repository.findByCombinationUrlAndUserEmail(URL, EMAIL); + } + + private UserCombinationFavoriteEntity whenAddToFavorites() throws Exception { + return repository.addToFavorite(URL, EMAIL); + } + + private void whenDeleteFavorite() throws Exception { + repository.deleteFromFavorites(URL, EMAIL); + } + + private PageDTO whenGetFavoritesPage() throws Exception { + return repository.getCombinationFavoritesForUserByEmail(EMAIL, 0); + } + + private void thenFavoriteShouldMatch(UserCombinationFavoriteModel model, String url) { + assertThat(model.getCombinationUrl()).isEqualTo(url); + } +} diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/UserGarmentFavoriteRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/UserGarmentFavoriteRepositoryImplTest.java new file mode 100644 index 0000000..36e25e9 --- /dev/null +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/UserGarmentFavoriteRepositoryImplTest.java @@ -0,0 +1,290 @@ +package com.outfitlab.project.infrastructure.repositories; + + +import com.outfitlab.project.domain.exceptions.FavoritesException; +import com.outfitlab.project.domain.exceptions.GarmentNotFoundException; +import com.outfitlab.project.domain.exceptions.UserGarmentFavoriteNotFoundException; +import com.outfitlab.project.domain.exceptions.UserNotFoundException; +import com.outfitlab.project.domain.model.PrendaModel; +import com.outfitlab.project.domain.model.UserGarmentFavoriteModel; +import com.outfitlab.project.domain.model.dto.PageDTO; +import com.outfitlab.project.infrastructure.model.PrendaEntity; +import com.outfitlab.project.infrastructure.model.UserEntity; +import com.outfitlab.project.infrastructure.model.UserGarmentFavoriteEntity; +import com.outfitlab.project.infrastructure.repositories.interfaces.GarmentJpaRepository; +import com.outfitlab.project.infrastructure.repositories.interfaces.UserGarmentFavoriteJpaRepository; +import com.outfitlab.project.infrastructure.repositories.interfaces.UserJpaRepository; +import static org.mockito.BDDMockito.given; + +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.test.context.ActiveProfiles; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +import static org.mockito.Mockito.*; + +@SpringBootTest +@ActiveProfiles("test") +class UserGarmentFavoriteRepositoryImplTest { + + @Mock + private UserGarmentFavoriteJpaRepository favoriteJpa; + + @Mock + private UserJpaRepository userJpa; + + @Mock + private GarmentJpaRepository garmentJpa; + + @InjectMocks + private UserGarmentFavoriteRepositoryImpl repository; + + @Test + void shouldReturnFavoriteWhenFavoriteExists() throws Exception { + String email = "user@mail.com"; + String code = "P001"; + + UserEntity user = givenUser(email); + PrendaEntity garment = givenGarment(code); + UserGarmentFavoriteEntity fav = givenFavorite(user, garment); + + givenFindFavoriteReturns(code, email, fav); + + UserGarmentFavoriteModel result = + repository.findByGarmentCodeAndUserEmail(code, email); + + assertThat(result.getGarment().getGarmentCode()).isEqualTo(code); + } + + @Test + void shouldThrowExceptionWhenFavoriteNotFound() { + String email = "user@mail.com"; + String code = "X999"; + + givenFindFavoriteReturns(code, email, null); + + assertThatThrownBy(() -> + whenFindByGarmentCodeAndUserEmail(code, email) + ) + .isInstanceOf(UserGarmentFavoriteNotFoundException.class) + .hasMessageContaining(code); + } + + @Test + void shouldAddFavoriteWhenUserAndGarmentExist() throws Exception { + String email = "user@mail.com"; + String code = "P123"; + + UserEntity user = givenUser(email); + PrendaEntity garment = givenGarment(code); + UserGarmentFavoriteEntity fav = givenFavorite(user, garment); + + givenUserExists(email, user); + givenGarmentExists(code, garment); + givenSaveReturns(fav); + + UserGarmentFavoriteEntity result = + repository.addToFavorite(code, email); + + assertThat(result.getGarment().getGarmentCode()).isEqualTo(code); + } + + @Test + void shouldThrowUserNotFoundWhenAddingFavorite() { + String email = "no@mail.com"; + String code = "P111"; + + givenUserExists(email, null); + + assertThatThrownBy(() -> + whenAddToFavorite(code, email) + ) + .isInstanceOf(UserNotFoundException.class) + .hasMessageContaining(email); + } + + @Test + void shouldThrowGarmentNotFoundWhenAddingFavorite() { + String email = "user@mail.com"; + String code = "P404"; + + givenUserExists(email, givenUser(email)); + givenGarmentExists(code, null); + + assertThatThrownBy(() -> + whenAddToFavorite(code, email) + ) + .isInstanceOf(GarmentNotFoundException.class) + .hasMessageContaining(code); + } + + @Test + void shouldDeleteFavoriteWhenExists() throws Exception { + String email = "user@mail.com"; + String code = "P001"; + + UserEntity user = givenUser(email); + PrendaEntity garment = givenGarment(code); + UserGarmentFavoriteEntity fav = givenFavorite(user, garment); + + givenUserExists(email, user); + givenGarmentExists(code, garment); + given(favoriteJpa.findByGarmentAndUser(garment, user)).willReturn(fav); + + whenDeleteFromFavorites(code, email); + + verify(favoriteJpa).delete(fav); + } + + @Test + void shouldThrowUserNotFoundWhenDeletingFavorite() { + String email = "x@mail.com"; + String code = "P001"; + + givenUserExists(email, null); + + assertThatThrownBy(() -> + whenDeleteFromFavorites(code, email) + ) + .isInstanceOf(UserNotFoundException.class) + .hasMessageContaining(email); + } + + @Test + void shouldThrowGarmentNotFoundWhenDeletingFavorite() { + String email = "user@mail.com"; + String code = "PXYZ"; + + givenUserExists(email, givenUser(email)); + givenGarmentExists(code, null); + + assertThatThrownBy(() -> + whenDeleteFromFavorites(code, email) + ) + .isInstanceOf(GarmentNotFoundException.class) + .hasMessageContaining(code); + } + + @Test + void shouldReturnFavoritesPageWhenExists() throws Exception { + String email = "user@mail.com"; + + UserEntity user = givenUser(email); + givenUserExists(email, user); + + PrendaEntity garment = givenGarment("P1"); + UserGarmentFavoriteEntity fav = givenFavorite(user, garment); + + Page page = + new PageImpl<>(List.of(fav)); + + givenFavoritesPage(email, page); + + PageDTO dto = + repository.getGarmentsFavoritesForUserByEmail(email, 0); + + assertThat(dto.getContent()).hasSize(1); + } + + @Test + void shouldThrowFavoritesExceptionWhenNoFavorites() { + String email = "user@mail.com"; + + givenUserExists(email, givenUser(email)); + + Page emptyPage = + new PageImpl<>(List.of()); + + givenFavoritesPage(email, emptyPage); + + assertThatThrownBy(() -> + whenGetFavorites(email, 0) + ) + .isInstanceOf(FavoritesException.class); + } + + @Test + void shouldThrowUserNotFoundWhenGettingFavorites() { + String email = "x@mail.com"; + givenUserExists(email, null); + + assertThatThrownBy(() -> + whenGetFavorites(email, 0) + ) + .isInstanceOf(UserNotFoundException.class); + } + + @Test + void shouldDeleteFavoritesRelatedToGarment() { + String code = "P001"; + + whenDeleteFavoritesRelated(code); + + verify(favoriteJpa).deleteAllByGarmentCode(code); + } + + private UserEntity givenUser(String email) { + return new UserEntity("user@mail.com"); + } + + private PrendaEntity givenGarment(String code) { + PrendaEntity p = new PrendaEntity(); + p.setGarmentCode(code); + return p; + } + + private UserGarmentFavoriteEntity givenFavorite(UserEntity u, PrendaEntity g) { + return new UserGarmentFavoriteEntity(u, g); + } + + private void givenUserExists(String email, UserEntity user) { + given(userJpa.findByEmail(email)).willReturn(user); + } + + private void givenGarmentExists(String code, PrendaEntity garment) { + given(garmentJpa.findByGarmentCode(code)).willReturn(garment); + } + + private void givenFindFavoriteReturns(String code, String email, UserGarmentFavoriteEntity fav) { + given(favoriteJpa.findByGarment_GarmentCodeAndUser_Email(code, email)).willReturn(fav); + } + + private void givenFavoritesPage(String email, Page page) { + given(favoriteJpa.findFavoritesByUserEmail(eq(email), any())).willReturn(page); + } + + private void givenSaveReturns(UserGarmentFavoriteEntity fav) { + given(favoriteJpa.save(any())).willReturn(fav); + } + + private void whenFindByGarmentCodeAndUserEmail(String code, String email) + throws UserGarmentFavoriteNotFoundException { + repository.findByGarmentCodeAndUserEmail(code, email); + } + + private void whenAddToFavorite(String code, String email) + throws Exception { + repository.addToFavorite(code, email); + } + + private void whenDeleteFromFavorites(String code, String email) + throws Exception { + repository.deleteFromFavorites(code, email); + } + + private void whenGetFavorites(String email, int page) + throws Exception { + repository.getGarmentsFavoritesForUserByEmail(email, page); + } + + private void whenDeleteFavoritesRelated(String code) { + repository.deleteFavoritesRelatedToGarment(code); + } +} \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/UserRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/UserRepositoryImplTest.java index 3edcbec..3855592 100644 --- a/src/test/java/com/outfitlab/project/infrastructure/repositories/UserRepositoryImplTest.java +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/UserRepositoryImplTest.java @@ -15,10 +15,10 @@ import static org.assertj.core.api.Assertions.*; import org.junit.jupiter.api.Test; -import java.util.ArrayList; import java.util.List; import java.util.Optional; +import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -33,274 +33,210 @@ class UserRepositoryImplTest { private UserRepositoryImpl userRepository; @Test - void givenExistingEmailWhenFindUserByEmailThenReturnUserModel() { - // ---------- GIVEN --------- - String email = "test@mail.com"; + void shouldReturnUserByEmail() { + givenExistingUserWithEmail("test@mail.com"); - UserEntity entity = new UserEntity(); - entity.setId(1L); - entity.setEmail(email); - entity.setName("Juan"); - entity.setLastName("Lopez"); + UserModel result = whenFindUserByEmail("test@mail.com"); - when(userJpaRepository.findByEmail(email)).thenReturn(entity); + thenReturnUserWithEmail(result, "test@mail.com"); + } - // ---------- WHEN -------- - UserModel result = userRepository.findUserByEmail(email); + @Test + void shouldThrowExceptionWhenEmailDoesNotExist() { + String email = "test15@mail.com"; - // ---------- THEN -------- - assertThat(result).isNotNull(); - assertThat(result.getEmail()).isEqualTo(email); - assertThat(result.getId()).isEqualTo(1L); + whenFindingByEmail(email, null); + + thenExceptionShouldBeThrown(email); verify(userJpaRepository).findByEmail(email); + verify(userJpaRepository, never()).save(any()); } @Test - void givenValidTokenWhenFindUserByVerificationTokenThenReturnUserModel() { + void shouldReturnUserModelWhenTokenIsValid() { String token = "abc123"; + givenUserWithVerificationToken(token, "test@mail.com", 1L); - UserEntity entity = new UserEntity(); - entity.setId(1L); - entity.setEmail("test@mail.com"); - entity.setVerificationToken(token); - - when(userJpaRepository.findByVerificationToken(token)).thenReturn(entity); - - UserModel result = userRepository.findUserByVerificationToken(token); + UserModel result = whenFindUserByVerificationToken(token); - assertThat(result).isNotNull(); - assertThat(result.getEmail()).isEqualTo("test@mail.com"); + thenUserShouldHaveEmail(result, "test@mail.com"); verify(userJpaRepository).findByVerificationToken(token); } + @Test - void givenInvalidTokenWhenFindUserByVerificationTokenThenThrowException() { + void shouldThrowExceptionWhenTokenIsInvalid() { String token = "def456"; - when(userJpaRepository.findByVerificationToken(token)).thenReturn(null); + givenNoUserWithToken(token); - assertThatThrownBy(() -> userRepository.findUserByVerificationToken(token)) - .isInstanceOf(UserNotFoundException.class); + thenExpectUserNotFoundByToken(token); verify(userJpaRepository).findByVerificationToken(token); } + @Test - void givenExistingIdWhenFindByIdThenReturnUserModel() { + void shouldReturnUserModelWhenIdExists() { Long id = 1L; + givenUserWithId(id, "test@mail.com"); - UserEntity entity = new UserEntity(); - entity.setId(id); - entity.setEmail("test@mail.com"); - - when(userJpaRepository.findById(id)).thenReturn(Optional.of(entity)); - - UserModel result = userRepository.findById(id); + UserModel result = whenFindById(id); - assertThat(result).isNotNull(); - assertThat(result.getId()).isEqualTo(id); - assertThat(result.getEmail()).isEqualTo("test@mail.com"); + thenUserShouldHaveId(result, id); + thenUserShouldHaveEmail(result, "test@mail.com"); verify(userJpaRepository).findById(id); } + @Test - void givenNonExistingIdWhenFindByIdThenThrowException() { + void shouldThrowExceptionWhenIdDoesNotExist() { Long id = 99L; - when(userJpaRepository.findById(id)).thenReturn(Optional.empty()); + givenNoUserWithId(id); - - assertThatThrownBy(() -> userRepository.findById(id)) - .isInstanceOf(UserNotFoundException.class); + thenExpectUserNotFoundById(id); verify(userJpaRepository).findById(id); } @Test - void givenUserModelWhenSaveUserThenPersistAndReturnUserModel() { - UserModel model = new UserModel(); - model.setEmail("save@mail.com"); - model.setName("Juan"); - - UserEntity savedEntity = new UserEntity(model); - savedEntity.setId(1L); + void shouldPersistAndReturnUserModelWhenSavingUser() { + UserModel model = givenUserModel("save@mail.com", "Juan"); + UserEntity savedEntity = givenSavedEntity(model, 1L); - when(userJpaRepository.save(any(UserEntity.class))) - .thenReturn(savedEntity); + whenSavingUserEntity(savedEntity); + UserModel result = whenSavingUser(model); - UserModel result = userRepository.saveUser(model); - System.out.println(result); - - assertThat(result).isNotNull(); - assertThat(result.getEmail()).isEqualTo("save@mail.com"); - assertThat(result.getId()).isNull(); + thenUserEmailShouldBe(result, "save@mail.com"); + thenUserIdShouldBeNull(result); verify(userJpaRepository).save(any(UserEntity.class)); } @Test - void givenExistingUsersWhenFindAllThenReturnUserList() { - List entities = new ArrayList<>(); - - UserEntity u1 = new UserEntity(); - u1.setId(1L); - u1.setEmail("u1@mail.com"); - - UserEntity u2 = new UserEntity(); - u2.setId(2L); - u2.setEmail("u2@mail.com"); - - entities.add(u1); - entities.add(u2); + void shouldReturnUserListWhenUsersExist() { + List entities = givenUserEntityList(); - when(userJpaRepository.findAll()).thenReturn(entities); - - List result = userRepository.findAll(); + whenFindingAllUsers(entities); + List result = whenCallingFindAll(); - assertThat(result).hasSize(2); - assertThat(result.get(0).getEmail()).isEqualTo("u1@mail.com"); - assertThat(result.get(1).getEmail()).isEqualTo("u2@mail.com"); + thenResultShouldHaveSize(result, 2); + thenUserEmailShouldBe(result.get(0), "u1@mail.com"); + thenUserEmailShouldBe(result.get(1), "u2@mail.com"); verify(userJpaRepository).findAll(); } @Test - void givenExistingEmailWhenDesactivateUserThenStatusIsSetToFalse() { + void shouldDeactivateUserWhenEmailExists() { String email = "test@mail.com"; + UserEntity entity = givenActiveUserEntity(email); - UserEntity entity = new UserEntity(); - entity.setEmail(email); - entity.setStatus(true); - - when(userJpaRepository.findByEmail(email)).thenReturn(entity); - - userRepository.desactivateUser(email); + whenFindingByEmail(email, entity); + whenDeactivatingUser(email); - assertThat(entity.isStatus()).isFalse(); + thenUserShouldBeDeactivated(entity); verify(userJpaRepository).findByEmail(email); verify(userJpaRepository).save(entity); } @Test - void givenNonExistingEmailWhenDesactivateUserThenThrowException() { - String email = "test15@mail.com"; - when(userJpaRepository.findByEmail(email)).thenReturn(null); + void shouldThrowExceptionWhenDeactivatingNonExistingUser() { + String email = "test17@mail.com"; - assertThatThrownBy(() -> userRepository.desactivateUser(email)) - .isInstanceOf(UserNotFoundException.class); + whenFindingByEmail(email, null); + thenDesactivateShouldThrowException(email); verify(userJpaRepository).findByEmail(email); verify(userJpaRepository, never()).save(any()); } @Test - void givenExistingEmailWhenActivateUserThenStatusTrueAndBrandApprovedTrue() { + void shouldActivateUserWhenEmailExists() { String email = "test@mail.com"; + UserEntity entity = givenInactiveAndUnapprovedUser(email); - UserEntity entity = new UserEntity(); - entity.setEmail(email); - entity.setStatus(false); - entity.setBrandApproved(false); - - when(userJpaRepository.findByEmail(email)).thenReturn(entity); - - userRepository.activateUser(email); + whenFindingByEmail(email, entity); + whenActivatingUser(email); - assertThat(entity.isStatus()).isTrue(); - assertThat(entity.isBrandApproved()).isTrue(); + thenUserShouldBeActive(entity); + thenUserBrandShouldBeApproved(entity); verify(userJpaRepository).findByEmail(email); verify(userJpaRepository).save(entity); } @Test - void givenNonExistingEmailWhenActivateUserThenThrowException() { + void shouldThrowExceptionWhenActivatingNonExistingUser() { String email = "test17@mail.com"; - when(userJpaRepository.findByEmail(email)).thenReturn(null); - assertThatThrownBy(() -> userRepository.activateUser(email)) - .isInstanceOf(UserNotFoundException.class); + whenFindingByEmail(email, null); + thenActivateShouldThrowException(email); verify(userJpaRepository).findByEmail(email); verify(userJpaRepository, never()).save(any()); } @Test - void givenExistingEmailWhenConvertToAdminThenRoleIsAdmin() { + void shouldConvertUserToAdminWhenEmailExists() { String email = "test@mail.com"; + UserEntity entity = givenUserWithRole(email, Role.USER); - UserEntity entity = new UserEntity(); - entity.setEmail(email); - entity.setRole(Role.USER); - - when(userJpaRepository.findByEmail(email)).thenReturn(entity); + whenFindingByEmail(email, entity); + whenConvertingToAdmin(email); - userRepository.convertToAdmin(email); - - assertThat(entity.getRole()).isEqualTo(Role.ADMIN); + thenRoleShouldBe(entity, Role.ADMIN); verify(userJpaRepository).save(entity); } @Test - void givenExistingEmailWhenConvertToUserThenRoleIsUser() { + void shouldConvertAdminToUserWhenEmailExists() { String email = "test@mail.com"; + UserEntity entity = givenUserWithRole(email, Role.ADMIN); - UserEntity entity = new UserEntity(); - entity.setEmail(email); - entity.setRole(Role.ADMIN); - - when(userJpaRepository.findByEmail(email)).thenReturn(entity); - - userRepository.convertToUser(email); + whenFindingByEmail(email, entity); + whenConvertingToUser(email); - assertThat(entity.getRole()).isEqualTo(Role.USER); + thenRoleShouldBe(entity, Role.USER); verify(userJpaRepository).save(entity); } @Test - void givenValidUserAndBrandWHENUpdateBrandUserTHENUserIsUpdatedCorrectly() { + void shouldUpdateBrandUserCorrectly() { String email = "user@mail.com"; String brandCode = "TESTBRAND"; + UserEntity user = givenUser(email); + MarcaEntity brand = givenBrand(brandCode); - UserEntity user = new UserEntity(); - user.setEmail(email); + whenFindingUserByEmail(email, user); + whenFindingBrandByCode(brandCode, brand); + whenSavingUser(user); - MarcaEntity brand = new MarcaEntity(); - brand.setCodigoMarca(brandCode); + whenUpdateBrandUser(email, brandCode); - when(userJpaRepository.findByEmail(email)).thenReturn(user); - when(brandJpaRepository.findByCodigoMarca(brandCode)).thenReturn(brand); - when(userJpaRepository.save(user)).thenReturn(user); - - userRepository.updateBrandUser(email, brandCode); - - assertThat(user.getBrand()).isEqualTo(brand); - assertThat(user.getRole()).isEqualTo(Role.BRAND); - assertThat(user.isBrandApproved()).isFalse(); + thenUserShouldHaveBrand(user, brand); + thenUserShouldHaveRole(user, Role.BRAND); + thenUserShouldBeUnapproved(user); verify(userJpaRepository).save(user); } @Test - void givenBrandCodeWhenGetEmailUserRelatedThenReturnEmail() { + void shouldReturnEmailWhenBrandUserExists() { String brandCode = "TESTBRAND"; + UserEntity user = givenUser("brand@mail.com"); - UserEntity user = new UserEntity(); - user.setEmail("brand@mail.com"); + whenFindingUserByBrandCode(brandCode, user); - when(userJpaRepository.findByBrand_CodigoMarca(brandCode)).thenReturn(user); + String email = whenGetEmailUserRelated(brandCode); - String email = userRepository.getEmailUserRelatedToBrandByBrandCode(brandCode); - - assertThat(email).isEqualTo("brand@mail.com"); + thenEmailShouldBe(email, "brand@mail.com"); verify(userJpaRepository).findByBrand_CodigoMarca(brandCode); } @Test - void givenUserDataWhenUpdateUserThenPersistChangesAndReturnModel() { - UserEntity entity = new UserEntity(); - entity.setEmail("old@mail.com"); - entity.setPassword("oldpass"); - entity.setUserImageUrl("old.png"); + void shouldUpdateUserCorrectlyWhenValidDataProvided() { + UserEntity entity = givenUserWithFullData(); - when(userJpaRepository.findByEmail("old@mail.com")).thenReturn(entity); - when(userJpaRepository.save(entity)).thenReturn(entity); + whenFindingUserByEmail("old@mail.com", entity); + whenSavingUser(entity); - UserModel result = userRepository.updateUser( + UserModel result = whenUpdateUser( "old@mail.com", "newName", "newLName", @@ -309,26 +245,19 @@ void givenUserDataWhenUpdateUserThenPersistChangesAndReturnModel() { "new.png" ); - assertThat(entity.getName()).isEqualTo("newName"); - assertThat(entity.getLastName()).isEqualTo("newLName"); - assertThat(entity.getEmail()).isEqualTo("new@mail.com"); - assertThat(entity.getPassword()).isEqualTo("newpass"); - assertThat(entity.getUserImageUrl()).isEqualTo("new.png"); - - assertThat(result.getEmail()).isEqualTo("new@mail.com"); + thenUserShouldHaveUpdatedFields(entity, "newName", "newLName", "new@mail.com", "newpass", "new.png"); + thenEmailShouldBe(result.getEmail(), "new@mail.com"); verify(userJpaRepository).save(entity); } @Test - void givenEmptyPasswordWhenUpdateUserThenPasswordIsNotModified() { - UserEntity entity = new UserEntity(); - entity.setEmail("old@mail.com"); - entity.setPassword("oldpass"); + void shouldNotModifyPasswordWhenEmptyPasswordProvided() { + UserEntity entity = givenUserWithOldPassword(); - when(userJpaRepository.findByEmail("old@mail.com")).thenReturn(entity); - when(userJpaRepository.save(entity)).thenReturn(entity); + whenFindingUserByEmail("old@mail.com", entity); + whenSavingUser(entity); - userRepository.updateUser( + whenUpdateUser( "old@mail.com", "newName", "newLNAme", @@ -337,7 +266,297 @@ void givenEmptyPasswordWhenUpdateUserThenPasswordIsNotModified() { "image.png" ); - assertThat(entity.getPassword()).isEqualTo("oldpass"); - assertThat(entity.getEmail()).isEqualTo("new@mail.com"); + thenPasswordShouldBe(entity, "oldpass"); + thenEmailShouldBe(entity.getEmail(), "new@mail.com"); + } + + private void givenExistingUserWithEmail(String email) { + UserEntity entity = new UserEntity(); + entity.setEmail(email); + when(userJpaRepository.findByEmail(email)).thenReturn(entity); + } + + private void givenUserDoesNotExist(String email) { + when(userJpaRepository.findByEmail(email)).thenReturn(null); + } + + private void givenUserWithVerificationToken(String token, String email, Long id) { + UserEntity entity = new UserEntity(); + entity.setId(id); + entity.setEmail(email); + entity.setVerificationToken(token); + + when(userJpaRepository.findByVerificationToken(token)).thenReturn(entity); + } + + private void givenNoUserWithToken(String token) { + when(userJpaRepository.findByVerificationToken(token)).thenReturn(null); + } + + private void givenUserWithId(Long id, String email) { + UserEntity entity = new UserEntity(); + entity.setId(id); + entity.setEmail(email); + + when(userJpaRepository.findById(id)).thenReturn(Optional.of(entity)); + } + + private void givenNoUserWithId(Long id) { + when(userJpaRepository.findById(id)).thenReturn(Optional.empty()); + } + + private UserModel whenFindUserByEmail(String email) { + return userRepository.findUserByEmail(email); + } + + private UserModel whenFindUserByVerificationToken(String token) { + return userRepository.findUserByVerificationToken(token); + } + + private UserModel whenFindById(Long id) { + return userRepository.findById(id); + } + + + private void thenReturnUserWithEmail(UserModel result, String expectedEmail) { + assertNotNull(result); + assertEquals(expectedEmail, result.getEmail()); } + + private void thenExpectUserNotFound(String email) { + assertThrows( + UserNotFoundException.class, + () -> userRepository.findUserByEmail(email) + ); + } + + private void thenUserShouldHaveEmail(UserModel result, String expectedEmail) { + assertThat(result).isNotNull(); + assertThat(result.getEmail()).isEqualTo(expectedEmail); + } + + private void thenUserShouldHaveId(UserModel result, Long expectedId) { + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(expectedId); + } + + private void thenExpectUserNotFoundByToken(String token) { + assertThatThrownBy(() -> userRepository.findUserByVerificationToken(token)) + .isInstanceOf(UserNotFoundException.class); + } + + private void thenExpectUserNotFoundById(Long id) { + assertThatThrownBy(() -> userRepository.findById(id)) + .isInstanceOf(UserNotFoundException.class); + } + + private UserModel givenUserModel(String email, String name) { + UserModel m = new UserModel(); + m.setEmail(email); + m.setName(name); + return m; + } + + private UserEntity givenSavedEntity(UserModel model, Long id) { + UserEntity e = new UserEntity(model); + e.setId(id); + return e; + } + + private List givenUserEntityList() { + UserEntity u1 = new UserEntity(); + u1.setId(1L); + u1.setEmail("u1@mail.com"); + + UserEntity u2 = new UserEntity(); + u2.setId(2L); + u2.setEmail("u2@mail.com"); + + return List.of(u1, u2); + } + + private UserEntity givenActiveUserEntity(String email) { + UserEntity e = new UserEntity(); + e.setEmail(email); + e.setStatus(true); + return e; + } + + private void whenSavingUserEntity(UserEntity saved) { + when(userJpaRepository.save(any(UserEntity.class))).thenReturn(saved); + } + + private UserModel whenSavingUser(UserModel model) { + return userRepository.saveUser(model); + } + + private void whenFindingAllUsers(List entities) { + when(userJpaRepository.findAll()).thenReturn(entities); + } + + private List whenCallingFindAll() { + return userRepository.findAll(); + } + + private void whenFindingByEmail(String email, UserEntity entity) { + when(userJpaRepository.findByEmail(email)).thenReturn(entity); + } + + private void whenDeactivatingUser(String email) { + userRepository.desactivateUser(email); + } + + private void thenUserEmailShouldBe(UserModel result, String expected) { + assertThat(result.getEmail()).isEqualTo(expected); + } + + private void thenUserIdShouldBeNull(UserModel result) { + assertThat(result.getId()).isNull(); + } + + private void thenResultShouldHaveSize(List list, int size) { + assertThat(list).hasSize(size); + } + + private void thenUserShouldBeDeactivated(UserEntity entity) { + assertThat(entity.isStatus()).isFalse(); + } + + private void thenExceptionShouldBeThrown(String email) { + assertThatThrownBy(() -> userRepository.findUserByEmail(email)) + .isInstanceOf(UserNotFoundException.class); + } + + + private UserEntity givenInactiveAndUnapprovedUser(String email) { + UserEntity e = new UserEntity(); + e.setEmail(email); + e.setStatus(false); + e.setBrandApproved(false); + return e; + } + + private UserEntity givenUserWithRole(String email, Role role) { + UserEntity e = new UserEntity(); + e.setEmail(email); + e.setRole(role); + return e; + } + + private void whenActivatingUser(String email) { + userRepository.activateUser(email); + } + + private void whenConvertingToAdmin(String email) { + userRepository.convertToAdmin(email); + } + + private void whenConvertingToUser(String email) { + userRepository.convertToUser(email); + } + + private void thenUserShouldBeActive(UserEntity entity) { + assertThat(entity.isStatus()).isTrue(); + } + + private void thenUserBrandShouldBeApproved(UserEntity entity) { + assertThat(entity.isBrandApproved()).isTrue(); + } + + private void thenDesactivateShouldThrowException(String email) { + assertThatThrownBy(() -> userRepository.desactivateUser(email)) + .isInstanceOf(UserNotFoundException.class); + } + private void thenActivateShouldThrowException(String email) { + assertThatThrownBy(() -> userRepository.activateUser(email)) + .isInstanceOf(UserNotFoundException.class); + } + + private void thenRoleShouldBe(UserEntity entity, Role expected) { + assertThat(entity.getRole()).isEqualTo(expected); + } + + private UserEntity givenUser(String email) { + UserEntity u = new UserEntity(); + u.setEmail(email); + return u; + } + + private MarcaEntity givenBrand(String code) { + MarcaEntity b = new MarcaEntity(); + b.setCodigoMarca(code); + return b; + } + + private UserEntity givenUserWithFullData() { + UserEntity e = new UserEntity(); + e.setEmail("old@mail.com"); + e.setPassword("oldpass"); + e.setUserImageUrl("old.png"); + return e; + } + + private UserEntity givenUserWithOldPassword() { + UserEntity e = new UserEntity(); + e.setEmail("old@mail.com"); + e.setPassword("oldpass"); + return e; + } + + private void whenFindingUserByEmail(String email, UserEntity user) { + when(userJpaRepository.findByEmail(email)).thenReturn(user); + } + + private void whenFindingBrandByCode(String code, MarcaEntity brand) { + when(brandJpaRepository.findByCodigoMarca(code)).thenReturn(brand); + } + + private void whenFindingUserByBrandCode(String code, UserEntity user) { + when(userJpaRepository.findByBrand_CodigoMarca(code)).thenReturn(user); + } + + private void whenSavingUser(UserEntity user) { + when(userJpaRepository.save(user)).thenReturn(user); + } + + private void whenUpdateBrandUser(String email, String code) { + userRepository.updateBrandUser(email, code); + } + + private String whenGetEmailUserRelated(String brandCode) { + return userRepository.getEmailUserRelatedToBrandByBrandCode(brandCode); + } + + private UserModel whenUpdateUser(String email, String n, String ln, String newEmail, String pass, String img) { + return userRepository.updateUser(email, n, ln, newEmail, pass, img); + } + + private void thenUserShouldHaveBrand(UserEntity user, MarcaEntity brand) { + assertThat(user.getBrand()).isEqualTo(brand); + } + + private void thenUserShouldHaveRole(UserEntity user, Role role) { + assertThat(user.getRole()).isEqualTo(role); + } + + private void thenUserShouldBeUnapproved(UserEntity user) { + assertThat(user.isBrandApproved()).isFalse(); + } + + private void thenEmailShouldBe(String actual, String expected) { + assertThat(actual).isEqualTo(expected); + } + + private void thenUserShouldHaveUpdatedFields(UserEntity e, String n, String ln, String email, String pass, String img) { + assertThat(e.getName()).isEqualTo(n); + assertThat(e.getLastName()).isEqualTo(ln); + assertThat(e.getEmail()).isEqualTo(email); + assertThat(e.getPassword()).isEqualTo(pass); + assertThat(e.getUserImageUrl()).isEqualTo(img); + } + + private void thenPasswordShouldBe(UserEntity e, String expected) { + assertThat(e.getPassword()).isEqualTo(expected); + } + } \ No newline at end of file diff --git a/src/test/java/com/outfitlab/project/infrastructure/repositories/UserSubscriptionRepositoryImplTest.java b/src/test/java/com/outfitlab/project/infrastructure/repositories/UserSubscriptionRepositoryImplTest.java index 95ee2b4..629eb39 100644 --- a/src/test/java/com/outfitlab/project/infrastructure/repositories/UserSubscriptionRepositoryImplTest.java +++ b/src/test/java/com/outfitlab/project/infrastructure/repositories/UserSubscriptionRepositoryImplTest.java @@ -34,183 +34,135 @@ class UserSubscriptionRepositoryImplTest { private UserSubscriptionRepositoryImpl repository; @Test - void givenEmailWhenFindByUserEmailThenReturnSubscriptionModel() throws SubscriptionNotFoundException { - UserEntity user = new UserEntity(); - user.setEmail("test@mail.com"); - - SubscriptionEntity plan = new SubscriptionEntity(); - plan.setPlanCode("BASIC"); - - UserSubscriptionEntity entity = new UserSubscriptionEntity(); - entity.setUser(user); - entity.setSubscription(plan); - entity.setCombinationsUsed(10); + void shouldReturnSubscriptionModelWhenEmailExists() throws SubscriptionNotFoundException { + String email = "test@mail.com"; + UserSubscriptionEntity entity = givenExistingSubscriptionReturningEntity(email, "BASIC", 10); - when(userSubscriptionJpaRepository.findByUserEmail("test@mail.com")) - .thenReturn(Optional.of(entity)); + whenFindingSubscriptionByUserEmail(email, entity); - UserSubscriptionModel result = repository.findByUserEmail("test@mail.com"); + UserSubscriptionModel result = whenFindByUserEmail(email); - assertThat(result).isNotNull(); - assertThat(result.getCombinationsUsed()).isEqualTo(10); - verify(userSubscriptionJpaRepository).findByUserEmail("test@mail.com"); + thenSubscriptionShouldHaveCombinationsUsed(result, 10); + verify(userSubscriptionJpaRepository).findByUserEmail(email); } @Test - void givenEmailWhenSubscriptionNotFoundThenThrowException() { - when(userSubscriptionJpaRepository.findByUserEmail("missing@mail.com")) - .thenReturn(Optional.empty()); + void shouldThrowExceptionWhenSubscriptionNotFound() { + String email = "missing@mail.com"; + + whenFindingSubscriptionByUserEmailEmpty(email); - assertThatThrownBy(() -> repository.findByUserEmail("missing@mail.com")) + assertThatThrownBy(() -> whenFindByUserEmail(email)) .isInstanceOf(SubscriptionNotFoundException.class) - .hasMessageContaining("missing@mail.com"); + .hasMessageContaining(email); - verify(userSubscriptionJpaRepository).findByUserEmail("missing@mail.com"); + verify(userSubscriptionJpaRepository).findByUserEmail(email); } - @Test - void givenSubscriptionModelWhenSaveThenPersistAndReturnModel() { - UserEntity user = new UserEntity(); - user.setEmail("user@mail.com"); - user.setId(5L); - - SubscriptionEntity plan = new SubscriptionEntity(); - plan.setPlanCode("PREMIUM"); - - when(userJpaRepository.findByEmail("user@mail.com")).thenReturn(user); - when(subscriptionJpaRepository.findByPlanCode("PREMIUM")).thenReturn(Optional.of(plan)); - - UserSubscriptionEntity savedEntity = new UserSubscriptionEntity(); - savedEntity.setUser(user); - savedEntity.setSubscription(plan); - savedEntity.setCombinationsUsed(3); - when(userSubscriptionJpaRepository.save(any(UserSubscriptionEntity.class))).thenReturn(savedEntity); + @Test + void shouldSaveSubscriptionCorrectly() { + String email = "user@mail.com"; + String planCode = "PREMIUM"; - UserSubscriptionModel model = new UserSubscriptionModel(); - model.setUserEmail("user@mail.com"); - model.setPlanCode("PREMIUM"); - model.setCombinationsUsed(3); + givenExistingUser(email, 5L); + givenExistingPlan(planCode); + givenSavedSubscription(3); - UserSubscriptionModel result = repository.save(model); + UserSubscriptionModel request = givenSubscriptionModel(email, planCode, 3); + UserSubscriptionModel result = whenSave(request); - assertThat(result).isNotNull(); - assertThat(result.getCombinationsUsed()).isEqualTo(3); + thenSubscriptionHasCombinations(result, 3); verify(userSubscriptionJpaRepository).save(any(UserSubscriptionEntity.class)); } @Test - void givenInvalidUserWhenSaveThenThrowException() { - UserSubscriptionModel model = new UserSubscriptionModel(); - model.setUserEmail("fail@mail.com"); + void shouldThrowExceptionWhenUserNotFoundOnSave() { + String email = "fail@mail.com"; + givenNoUser(email); - when(userJpaRepository.findByEmail("fail@mail.com")).thenReturn(null); + UserSubscriptionModel request = givenSubscriptionModel(email, null, 0); - assertThatThrownBy(() -> repository.save(model)) + assertThatThrownBy(() -> whenSave(request)) .isInstanceOf(RuntimeException.class) .hasMessageContaining("Usuario no encontrado"); } @Test - void givenInvalidPlanWhenSaveThenThrowException() { - UserSubscriptionModel model = new UserSubscriptionModel(); - model.setUserEmail("user@mail.com"); - model.setPlanCode("PLAN A"); + void shouldThrowExceptionWhenPlanNotFoundOnSave() { + String email = "user@mail.com"; + String planCode = "INVALID_PLAN"; + + givenExistingUser(email, 1L); + givenNoPlan(planCode); - when(userJpaRepository.findByEmail("user@mail.com")).thenReturn(new UserEntity()); - when(subscriptionJpaRepository.findByPlanCode("PLAN A")).thenReturn(Optional.empty()); + UserSubscriptionModel request = givenSubscriptionModel(email, planCode, 0); - assertThatThrownBy(() -> repository.save(model)) + assertThatThrownBy(() -> whenSave(request)) .isInstanceOf(RuntimeException.class) .hasMessageContaining("Plan no encontrado"); } @Test - void givenSubscriptionModelWhenUpdateThenUpdateAndReturnModel() { - SubscriptionEntity oldPlan = new SubscriptionEntity(); - oldPlan.setPlanCode("ESTANDAR"); - - UserSubscriptionEntity existingUserSubs = new UserSubscriptionEntity(); - existingUserSubs.setCombinationsUsed(1); - existingUserSubs.setUser(new UserEntity("test@mail.com")); - existingUserSubs.setSubscription(oldPlan); - - SubscriptionEntity newPlan = new SubscriptionEntity(); - newPlan.setPlanCode("PRO"); - - when(userSubscriptionJpaRepository.findByUserEmail("test@mail.com")) - .thenReturn(Optional.of(existingUserSubs)); - when(subscriptionJpaRepository.findByPlanCode("ESTANDAR")) - .thenReturn(Optional.of(oldPlan)); - when(subscriptionJpaRepository.findByPlanCode("PRO")) - .thenReturn(Optional.of(newPlan)); - when(userSubscriptionJpaRepository.save(existingUserSubs)).thenReturn(existingUserSubs); + void shouldUpdateSubscriptionCorrectly() { + String email = "test@mail.com"; - UserSubscriptionModel model = new UserSubscriptionModel(); - model.setUserEmail("test@mail.com"); - model.setPlanCode("PRO"); - model.setCombinationsUsed(7); + givenExistingSubscription(email, "ESTANDAR", 1); + givenExistingPlan("ESTANDAR"); + givenExistingPlan("PRO"); - UserSubscriptionModel result = repository.update(model); + UserSubscriptionModel request = givenSubscriptionModel(email, "PRO", 7); + UserSubscriptionModel result = whenUpdate(request); - assertThat(existingUserSubs.getSubscription()).isEqualTo(newPlan); - assertThat(existingUserSubs.getCombinationsUsed()).isEqualTo(7); - assertThat(result).isNotNull(); + thenSubscriptionPlanIs(email, "PRO"); + thenSubscriptionHasCombinations(result, 7); } @Test - void givenNonExistingSubscriptionWhenUpdateThenThrowException() { - UserSubscriptionModel model = new UserSubscriptionModel(); - model.setUserEmail("noexiste@mail.com"); + void shouldThrowExceptionWhenSubscriptionNotFoundOnUpdate() { + String email = "noexiste@mail.com"; + UserSubscriptionModel model = givenSubscriptionModel(email, null, 0); - when(userSubscriptionJpaRepository.findByUserEmail("noexiste@mail.com")) - .thenReturn(Optional.empty()); + givenNoSubscription(email); - assertThatThrownBy(() -> repository.update(model)) + assertThatThrownBy(() -> whenUpdate(model)) .isInstanceOf(RuntimeException.class) .hasMessageContaining("Suscripción no encontrada"); } @Test - void givenInvalidPlanWhenUpdateThenThrowException() { - UserSubscriptionModel model = new UserSubscriptionModel(); - model.setUserEmail("test@mail.com"); - model.setPlanCode("TURBO"); + void shouldThrowExceptionWhenPlanNotFoundOnUpdate() { + String email = "test@mail.com"; + String planCode = "TURBO"; - when(userSubscriptionJpaRepository.findByUserEmail("test@mail.com")) - .thenReturn(Optional.of(new UserSubscriptionEntity())); - when(subscriptionJpaRepository.findByPlanCode("TURBO")) - .thenReturn(Optional.empty()); + givenExistingSubscription(email, "ANY_PLAN", 0); + givenNoPlan(planCode); + + UserSubscriptionModel model = givenSubscriptionModel(email, planCode, 0); - assertThatThrownBy(() -> repository.update(model)) + assertThatThrownBy(() -> whenUpdate(model)) .isInstanceOf(RuntimeException.class) .hasMessageContaining("Plan no encontrado"); } @Test - void givenEmailAndCombinationsWhenIncrementCounterThenCallsIncrementCombinations() { - UserEntity user = new UserEntity(); - user.setId(1L); - - when(userJpaRepository.findByEmail("test@mail.com")).thenReturn(user); + void shouldIncrementCombinationsCounter() { + String email = "test@mail.com"; + givenExistingUser(email, 1L); - repository.incrementCounter("test@mail.com", "combinations"); + whenIncrementCounter(email, "combinations"); - verify(userSubscriptionJpaRepository).incrementCombinationsByUserId(1L); + thenIncrementCombinationsWasCalled(1L); } - @Test - void givenEmailAndFavoritesWhenIncrementCounterThenCallsIncrementFavorites() { - UserEntity user = new UserEntity(); - user.setId(1L); - - when(userJpaRepository.findByEmail("test@mail.com")).thenReturn(user); + void shouldIncrementModelsCounter() { + String email = "test@mail.com"; + givenExistingUser(email, 1L); - repository.incrementCounter("test@mail.com", "favorites"); + whenIncrementCounter(email, "3d_models"); - verify(userSubscriptionJpaRepository).incrementFavoritesByUserId(1L); + thenIncrementModelsWasCalled(1L); } - @Test void givenEmailAnd3dModelsWhenIncrementCounterThenCallsIncrementModels() { UserEntity user = new UserEntity(); @@ -224,40 +176,193 @@ void givenEmailAnd3dModelsWhenIncrementCounterThenCallsIncrementModels() { } @Test - void givenNonexistentUserWhenIncrementCounterThenThrowException() { - when(userJpaRepository.findByEmail("noexiste@mail.com")).thenReturn(null); + void shouldThrowExceptionWhenUserNotFoundOnCounterIncrement() { + String email = "noexiste@mail.com"; + givenNoUser(email); - assertThatThrownBy(() -> repository.incrementCounter("noexiste@mail.com", "combinations")) + assertThatThrownBy(() -> whenIncrementCounter(email, "combinations")) .isInstanceOf(RuntimeException.class) .hasMessageContaining("Usuario no encontrado"); } @Test - void givenEmailAndFavoritesWhenDecrementCounterThenCallsDecrementFavorites() { - UserEntity user = new UserEntity(); - user.setId(2L); + void shouldDecrementFavoritesWhenCounterIsFavorites() { + givenUserWithId("test@mail.com", 2L); - when(userJpaRepository.findByEmail("test@mail.com")).thenReturn(user); + whenDecrementCounter("test@mail.com", "favorites"); - repository.decrementCounter("test@mail.com", "favorites"); - - verify(userSubscriptionJpaRepository).decrementFavoritesByUserId(2L); + thenDecrementFavoritesWasCalled(2L); } @Test - void givenNonFavoriteCounterWhenDecrementCounterThenDoNothing() { - repository.decrementCounter("test@mail.com", "models"); + void shouldDoNothingWhenCounterIsNotFavorite() { + // No hace falta givenUser porque no lo usa internamente + whenDecrementCounter("test@mail.com", "models"); - verify(userSubscriptionJpaRepository, never()).decrementFavoritesByUserId(anyLong()); + thenDecrementFavoritesWasNeverCalled(); } @Test - void givenNonexistentUserWhenDecrementCounterThenThrowException() { - when(userJpaRepository.findByEmail("noexiste@mail.com")).thenReturn(null); + void shouldThrowExceptionWhenUserDoesNotExistOnDecrement() { + givenNoUserForEmail("noexiste@mail.com"); - assertThatThrownBy(() -> repository.decrementCounter("noexiste@mail.com", "favorites")) + assertThatThrownBy(() -> whenDecrementCounter("noexiste@mail.com", "favorites")) .isInstanceOf(RuntimeException.class) .hasMessageContaining("Usuario no encontrado"); } + private void whenFindingSubscriptionByUserEmail(String email, UserSubscriptionEntity entity) { + when(userSubscriptionJpaRepository.findByUserEmail(email)).thenReturn(Optional.of(entity)); + } + + private void whenFindingSubscriptionByUserEmailEmpty(String email) { + when(userSubscriptionJpaRepository.findByUserEmail(email)).thenReturn(Optional.empty()); + } + + private UserSubscriptionModel whenFindByUserEmail(String email) throws SubscriptionNotFoundException { + return repository.findByUserEmail(email); + } + + private void thenSubscriptionShouldHaveCombinationsUsed(UserSubscriptionModel model, int expected) { + assertThat(model).isNotNull(); + assertThat(model.getCombinationsUsed()).isEqualTo(expected); + } + + private UserSubscriptionEntity givenSubscriptionEntity(String email, String planCode, Long userId, int combinations) { + UserEntity user = new UserEntity(); + user.setId(userId); + user.setEmail(email); + + SubscriptionEntity plan = new SubscriptionEntity("ESTANDAR", planCode); + plan.setPlanCode(planCode); + + UserSubscriptionEntity entity = new UserSubscriptionEntity(); + entity.setId(99L); + entity.setUser(user); + entity.setSubscription(plan); + entity.setCombinationsUsed(combinations); + + return entity; + } + + private void givenExistingSubscription(String email, String planCode, int combinations) { + UserSubscriptionEntity entity = givenSubscriptionEntity(email, planCode, 10L, combinations); + when(userSubscriptionJpaRepository.findByUserEmail(email)).thenReturn(Optional.of(entity)); + when(userSubscriptionJpaRepository.save(any(UserSubscriptionEntity.class))) + .thenAnswer(invocation -> invocation.getArgument(0)); + } + + private UserSubscriptionEntity givenExistingSubscriptionReturningEntity(String email, String planCode, int combinations) { + UserSubscriptionEntity entity = givenSubscriptionEntity(email, planCode, 10L, combinations); + when(userSubscriptionJpaRepository.findByUserEmail(email)) + .thenReturn(Optional.of(entity)); + return entity; + } + private void givenExistingPlan(String planCode) { + SubscriptionEntity plan = new SubscriptionEntity("ESTANDAR", planCode); + plan.setPlanCode(planCode); + when(subscriptionJpaRepository.findByPlanCode(planCode)).thenReturn(Optional.of(plan)); + } + + private void givenSavedSubscription(int combinations) { + UserSubscriptionEntity entity = givenSubscriptionEntity("user@mail.com", "PREMIUM", 5L, combinations); + when(userSubscriptionJpaRepository.save(any(UserSubscriptionEntity.class))) + .thenReturn(entity); + } + + private void givenNoSubscription(String email) { + when(userSubscriptionJpaRepository.findByUserEmail(email)) + .thenReturn(Optional.empty()); + } + + private void givenExistingUser(String email, Long id) { + UserEntity user = new UserEntity(); + user.setEmail(email); + user.setId(id); + when(userJpaRepository.findByEmail(email)).thenReturn(user); + } + + private void givenNoUser(String email) { + when(userJpaRepository.findByEmail(email)).thenReturn(null); + } + + + private void givenNoPlan(String planCode) { + when(subscriptionJpaRepository.findByPlanCode(planCode)) + .thenReturn(Optional.empty()); + } + + private UserSubscriptionModel givenSubscriptionModel(String email, String planCode, int used) { + UserSubscriptionModel model = new UserSubscriptionModel(); + model.setUserEmail(email); + model.setPlanCode(planCode); + model.setCombinationsUsed(used); + return model; + } + + private UserSubscriptionModel whenSave(UserSubscriptionModel model) { + return repository.save(model); + } + + private UserSubscriptionModel whenUpdate(UserSubscriptionModel model) { + return repository.update(model); + } + + private void thenSubscriptionHasCombinations(UserSubscriptionModel result, int expected) { + assertThat(result).isNotNull(); + assertThat(result.getCombinationsUsed()).isEqualTo(expected); + } + + private void thenSubscriptionPlanIs(String email, String expectedPlan) { + SubscriptionEntity subscription = + userSubscriptionJpaRepository.findByUserEmail(email).get().getSubscription(); + + assertThat(subscription.getPlanCode()).isEqualTo(expectedPlan); + } + + private void givenPlanExists(String planCode) { + SubscriptionEntity plan = new SubscriptionEntity("ESTANDAR", planCode); + plan.setPlanCode(planCode); + when(subscriptionJpaRepository.findByPlanCode(planCode)).thenReturn(Optional.of(plan)); + } + + private void whenIncrementCounter(String email, String counterType) { + repository.incrementCounter(email, counterType); + } + + private void whenDecrementCounter(String email, String counterType) { + repository.decrementCounter(email, counterType); + } + + private void thenIncrementCombinationsWasCalled(Long userId) { + verify(userSubscriptionJpaRepository).incrementCombinationsByUserId(userId); + } + + private void thenIncrementFavoritesWasCalled(Long userId) { + verify(userSubscriptionJpaRepository).incrementFavoritesByUserId(userId); + } + + private void thenIncrementModelsWasCalled(Long userId) { + verify(userSubscriptionJpaRepository).incrementModelsByUserId(userId); + } + + private void thenDecrementFavoritesWasCalled(Long userId) { + verify(userSubscriptionJpaRepository).decrementFavoritesByUserId(userId); + } + + private void givenUserWithId(String email, Long id) { + UserEntity user = new UserEntity(); + user.setEmail(email); + user.setId(id); + + when(userJpaRepository.findByEmail(email)).thenReturn(user); + } + + private void givenNoUserForEmail(String email) { + when(userJpaRepository.findByEmail(email)).thenReturn(null); + } + + private void thenDecrementFavoritesWasNeverCalled() { + verify(userSubscriptionJpaRepository, never()).decrementFavoritesByUserId(anyLong()); + } } \ No newline at end of file