From cb5a96dcd7a8da6d32b4709b126c1fcd813fb505 Mon Sep 17 00:00:00 2001 From: Almami679 Date: Thu, 24 Apr 2025 13:18:02 +0200 Subject: [PATCH 01/63] =?UTF-8?q?a=C3=B1adido=20nuevo=20parametro=20al=20e?= =?UTF-8?q?num=20de=20ChallengeStatus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/itachallenge/user/document/enums/ChallengeStatus.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java b/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java index 2ce61bbbe..671fed6f4 100644 --- a/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java +++ b/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java @@ -3,7 +3,8 @@ import lombok.Getter; @Getter -public enum ChallengeStatus {ENDED("ENDED"); +public enum ChallengeStatus {ENDED("ENDED"), + IN_PROGRESS("IN_PROGRESS"); private final String value; From 708e64b26aefc173bbbb18e59d30a4162f54408b Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Sun, 27 Apr 2025 18:21:24 +0200 Subject: [PATCH 02/63] creation SolvedDto --- .../com/itachallenge/challenge/dto/SolvedDto.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 itachallenge-challenge/src/main/java/com/itachallenge/challenge/dto/SolvedDto.java diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/dto/SolvedDto.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/dto/SolvedDto.java new file mode 100644 index 000000000..7c46a0421 --- /dev/null +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/dto/SolvedDto.java @@ -0,0 +1,14 @@ +package com.itachallenge.challenge.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class SolvedDto { + + private boolean isSolved; + + private int timesSolved; + +} From d1726596e81d901910084e40665d918dd01987d0 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Sun, 27 Apr 2025 18:37:17 +0200 Subject: [PATCH 03/63] added field times_solved to ChallengeDto --- .../java/com/itachallenge/challenge/dto/ChallengeDto.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/dto/ChallengeDto.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/dto/ChallengeDto.java index 139ac07f7..41ff16c9d 100755 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/dto/ChallengeDto.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/dto/ChallengeDto.java @@ -63,4 +63,8 @@ public class ChallengeDto { @JsonProperty(index = 11) private Integer timesBookmark; + + @JsonProperty(index = 12) + private Integer timesSolved; + } From f65f94a177c522edb0f8b9f93fb1e93b10529bd2 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Sun, 27 Apr 2025 18:40:53 +0200 Subject: [PATCH 04/63] added field timesSolved and method increase to ChallengeDocument --- .../itachallenge/challenge/document/ChallengeDocument.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/document/ChallengeDocument.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/document/ChallengeDocument.java index 935cb5847..b434ac0b4 100755 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/document/ChallengeDocument.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/document/ChallengeDocument.java @@ -48,6 +48,9 @@ public class ChallengeDocument { @Field(name="times_bookmark") private Integer timesBookmark; + @Field(name="times_solved") + private Integer timesSolved; + @Field(name = "tags") private List tags; @@ -77,4 +80,8 @@ public void increaseTimesBookmark() { timesBookmark = timesBookmark == null ? 1 : timesBookmark + 1; } + public void increaseTimesSolved() { + timesSolved = timesSolved == null ? 1 : timesSolved + 1; + } + } From 392c8d2e1271ccc06ece8cc1b0849073fee34fdb Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Sun, 27 Apr 2025 18:41:28 +0200 Subject: [PATCH 05/63] added solved to the enum UserChallengeActionType --- .../itachallenge/challenge/enums/UserChallengeActionType.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/enums/UserChallengeActionType.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/enums/UserChallengeActionType.java index 31269c178..b53a8f0b6 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/enums/UserChallengeActionType.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/enums/UserChallengeActionType.java @@ -2,5 +2,6 @@ public enum UserChallengeActionType { BOOKMARKS, - FAVORITES + FAVORITES, + SOLVED } From 60f89f9e961b16685103dd92e2b8dab47123789b Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Sun, 27 Apr 2025 20:53:07 +0200 Subject: [PATCH 06/63] added getTimesSolved to method convertDtoToDocument --- .../itachallenge/challenge/helper/DocumentToDtoConverter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/helper/DocumentToDtoConverter.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/helper/DocumentToDtoConverter.java index 876f99785..83125e83e 100755 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/helper/DocumentToDtoConverter.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/helper/DocumentToDtoConverter.java @@ -54,7 +54,8 @@ protected String convert(LocalDateTime creationDateFromDocument) { .addMapping(ChallengeDocument::getTitle, ChallengeDto::setTitle) .addMapping(ChallengeDocument::getTimesFavorite, ChallengeDto::setTimesFavorite) .addMapping(ChallengeDocument::getTags, ChallengeDto::setTags) - .addMapping(ChallengeDocument::getTimesBookmark, ChallengeDto::setTimesBookmark); + .addMapping(ChallengeDocument::getTimesBookmark, ChallengeDto::setTimesBookmark) + .addMapping(ChallengeDocument::getTimesSolved, ChallengeDto::setTimesSolved); mapper.addConverter(converterFromLocalDateTimeToString); } From 3fb18ae0b36cb53b02c97918faae3192c7f3d851 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Mon, 28 Apr 2025 11:33:42 +0200 Subject: [PATCH 07/63] [feat] Obtener recursos asociados a un challengeId (servicio + repositorio + test) --- .../repository/ResourceRepository.java | 1 + .../challenge/service/IResourceService.java | 3 + .../service/ResourceServiceImpl.java | 17 ++++ .../repository/ResourceRepositoryTest.java | 46 ++++++++++ .../service/ResourceServiceImplTest.java | 86 +++++++++++++++++++ 5 files changed, 153 insertions(+) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/repository/ResourceRepository.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/repository/ResourceRepository.java index e989cb36f..18dfee3f8 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/repository/ResourceRepository.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/repository/ResourceRepository.java @@ -25,4 +25,5 @@ public interface ResourceRepository extends ReactiveSortingRepository saveAll(Flux resourceDocumentFlux); Mono deleteAll(); + Flux findByChallengeIdsContaining(UUID challengeId); } diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IResourceService.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IResourceService.java index 831dba342..9f4169d72 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IResourceService.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IResourceService.java @@ -1,12 +1,15 @@ package com.itachallenge.challenge.service; import com.itachallenge.challenge.dto.*; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.util.UUID; public interface IResourceService { Mono createResource(ResourceDto resourceDto); + Flux getResourcesByChallengeId(UUID challengeId); } diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java index e3b5fd5de..25df1d9d4 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java @@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.CacheEvict; import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.*; @@ -135,4 +136,20 @@ private Mono saveResource(ResourceDto resourceDto) { }) .doOnError(error -> log.error("Error occurred when creating resource {}", error.getMessage())); } + + + public Flux getResourcesByChallengeId(UUID challengeId) { + + if (challengeId == null) { + return Flux.error(new IllegalArgumentException("Challenge ID cannot be null")); + } + + + return resourceRepository.findByChallengeIdsContaining(challengeId) + .map(resourceDoc -> resourceConverter.convertDocumentToDto(resourceDoc, ResourceDto.class)) + .onErrorResume(error -> { + log.error("Error fetching resources: {}", error.getMessage()); + return Flux.error(new RuntimeException("Server error while fetching resources")); + }); + } } \ No newline at end of file diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java index 88a14a770..7d8a8e53f 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java @@ -251,4 +251,50 @@ void deleteAllResourcesTest() { .expectNext(0L) .verifyComplete(); } + + // Revisar + limpiar esto + // este test Verifica que el método findByChallengeIdsContaining + // del repositorio devuelve únicamente los recursos que contienen + // el challengeId especificado en su lista de retos asociados. + @Test + void findByChallengeIdsContaining_WhenChallengeIdExists_ReturnsResources() { + + UUID targetChallengeId = UUID.fromString("8ecbfe54-fec8-11ed-be56-0242ac120002"); + + + ResourceDocument resourceWithTargetChallenge = ResourceDocument.builder() + .resourceId(uuid1) + .title("Title1") + .description("Description1") + .url("http://example.com/resource1") + .topic(Topic.DEBUGGING) + .contentType(ResourceContentType.BLOG) + .challengeIds(List.of(targetChallengeId)) + .build(); + + ResourceDocument resourceWithoutTargetChallenge = ResourceDocument.builder() + .resourceId(uuid2) + .title("Title2") + .description("Description2") + .url("http://example.com/resource2") + .topic(Topic.DEBUGGING) + .contentType(ResourceContentType.BLOG) + .challengeIds(List.of(UUID.randomUUID())) + .build(); + + + resourceRepository.deleteAll().block(); + resourceRepository.saveAll(Flux.just(resourceWithTargetChallenge, resourceWithoutTargetChallenge)).blockLast(); + + + Flux result = resourceRepository.findByChallengeIdsContaining(targetChallengeId); + + + StepVerifier.create(result) + .expectNextMatches(resource -> + resource.getChallengeIds().contains(targetChallengeId) && + resource.getResourceId().equals(uuid1) + ) + .verifyComplete(); + } } diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java index 2a2bba86d..0d1a84bc7 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java @@ -14,6 +14,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import static org.mockito.ArgumentMatchers.*; @@ -416,5 +417,90 @@ void createResource_WithFailedConversion_ShouldThrowError() { .verify(); } + // ID CON recursos + @Test + void getResourcesByChallengeId_WhenResourcesExist_ReturnsFluxOfResources() { + + UUID challengeId = UUID.randomUUID(); + UUID resourceId1 = UUID.randomUUID(); + UUID resourceId2 = UUID.randomUUID(); + + + ResourceDocument doc1 = new ResourceDocument( + resourceId1, "Resource 1", "Desc 1", "https://example.com/1", + Topic.DEBUGGING, ResourceContentType.VIDEO, List.of(challengeId), AssociationType.ALLSAMETOPIC + ); + ResourceDocument doc2 = new ResourceDocument( + resourceId2, "Resource 2", "Desc 2", "https://example.com/2", + Topic.COMPONENTS, ResourceContentType.BLOG, List.of(challengeId), AssociationType.CHOOSE + ); + + ResourceDto dto1 = new ResourceDto(); + dto1.setResourceId(resourceId1); + dto1.setTitle("Resource 1"); + + + ResourceDto dto2 = new ResourceDto(); + dto2.setResourceId(resourceId2); + dto2.setTitle("Resource 2"); + + + when(resourceRepository.findByChallengeIdsContaining(challengeId)) + .thenReturn(Flux.just(doc1, doc2)); + when(resourceConverter.convertDocumentToDto(doc1, ResourceDto.class)).thenReturn(dto1); + when(resourceConverter.convertDocumentToDto(doc2, ResourceDto.class)).thenReturn(dto2); + + + StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) + .expectNext(dto1) + .expectNext(dto2) + .verifyComplete(); + } + + // Valido SIN recursos + @Test + void getResourcesByChallengeId_WhenNoResourcesExist_ReturnsEmptyFlux() { + + UUID challengeId = UUID.randomUUID(); + when(resourceRepository.findByChallengeIdsContaining(challengeId)) + .thenReturn(Flux.empty()); + + + StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) + .expectNextCount(0) + .verifyComplete(); + } + + //ID Nulo + @Test + void getResourcesByChallengeId_WhenIdIsNull_ThrowsIllegalArgumentException() { + + StepVerifier.create(resourceService.getResourcesByChallengeId(null)) + .expectErrorMatches(ex -> + ex instanceof IllegalArgumentException && + ex.getMessage().equals("Challenge ID cannot be null") + ) + .verify(); + } + + //Error en el repo + @Test + void getResourcesByChallengeId_WhenRepositoryFails_PropagatesError() { + + UUID challengeId = UUID.randomUUID(); + when(resourceRepository.findByChallengeIdsContaining(challengeId)) + .thenReturn(Flux.error(new RuntimeException("DB Connection Failed"))); + + + StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) + .expectErrorMatches(ex -> + ex instanceof RuntimeException && + ex.getMessage().equals("Server error while fetching resources") + ) + .verify(); + } + + + } From b6dfe9e2415db946454ebde604b99daf90edf21c Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Mon, 28 Apr 2025 23:48:11 +0200 Subject: [PATCH 08/63] updated tests --- .../challenge/document/ChallengeTest.java | 32 +++++++++++++++++-- .../ChallengeDocumentToDtoConverterTest.java | 5 +-- .../integration/ChallengeIntegrationTest.java | 4 +-- .../repository/ChallengeRepositoryTest.java | 9 +++--- .../service/ChallengeServiceImplTest.java | 6 ++-- 5 files changed, 43 insertions(+), 13 deletions(-) diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java index 446615ae8..60e8d9c77 100755 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java @@ -28,6 +28,7 @@ void getUuid() { Topic.LISTS, null, null, + null, tags); assertEquals(uuid, challenge.getUuid()); } @@ -45,6 +46,7 @@ void getTitle() { Topic.COMPONENTS, null, null, + null, tags); assertEquals(expectedTitle, challenge.getTitle()); } @@ -62,6 +64,7 @@ void getLevel() { Topic.COMPONENTS, null, null, + null, tags); assertEquals(level, challenge.getLevel()); } @@ -79,6 +82,7 @@ void getCreationDate() { Topic.COMPONENTS, null, null, + null, tags); assertTrue(creationDate.truncatedTo(ChronoUnit.SECONDS).isEqual(challenge.getCreationDate().truncatedTo(ChronoUnit.SECONDS))); } @@ -96,6 +100,7 @@ void getDetail() { Topic.COMPONENTS, null, null, + null, tags); assertEquals(detail, challenge.getDetail()); } @@ -108,7 +113,7 @@ void getLanguages() { "https://res.cloudinary.com/itachallenge/image/upload/v1739361249/language_icon_Javascript_asgn04.svg"), new LanguageDocument(uuid2, "Python", "https://res.cloudinary.com/itachallenge/image/upload/v1739361249/language_icon_Python_rphody.svg")); - ChallengeDocument challenge = new ChallengeDocument(null, null, null, null, null, languages, null, Topic.COMPONENTS, null, null, tags); + ChallengeDocument challenge = new ChallengeDocument(null, null, null, null, null, languages, null, Topic.COMPONENTS, null, null,null, tags); assertEquals(languages, challenge.getLanguages()); } @@ -126,6 +131,7 @@ void getSolutions() { Topic.COMPONENTS, null, null, + null, tags); assertEquals(solutions, challenge.getSolutions()); } @@ -144,6 +150,7 @@ void getTimesFavorite() { Topic.COMPONENTS, timesFavorite, null, + null, tags); assertEquals(timesFavorite, challenge.getTimesFavorite()); } @@ -152,10 +159,29 @@ void getTimesFavorite() { void getTimesBookmark(){ int timesBookmark = 30; - ChallengeDocument challenge = new ChallengeDocument(null, null, null, null, null, null, null, Topic.COMPONENTS, null, timesBookmark, tags); + ChallengeDocument challenge = new ChallengeDocument(null, null, null, null, null, null, null, Topic.COMPONENTS, null, timesBookmark,null, tags); assertEquals(timesBookmark, challenge.getTimesBookmark()); } + @Test + void getTimesSolved() { + int timesSolved = 21; + + ChallengeDocument challenge = new ChallengeDocument(null, + null, + null, + null, + null, + null, + null, + Topic.COMPONENTS, + null, + null, + timesSolved, + tags); + assertEquals(timesSolved, challenge.getTimesSolved()); + } + @Test void getTagsTest() { UUID uuid = UUID.randomUUID(); @@ -172,6 +198,7 @@ void getTagsTest() { Topic.COMPONENTS, 20, null, + null, List.of(tag.getIdTag()) ); @@ -190,6 +217,7 @@ void setTagsTest() { Topic.COMPONENTS, 20, null, + null, new ArrayList(List.of(firstTag.getIdTag())) { } ); diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/helper/ChallengeDocumentToDtoConverterTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/helper/ChallengeDocumentToDtoConverterTest.java index a187bd8cb..7ac1b4fa2 100755 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/helper/ChallengeDocumentToDtoConverterTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/helper/ChallengeDocumentToDtoConverterTest.java @@ -59,12 +59,13 @@ public void setUp() { Topic topic = Topic.DEBUGGING; int timesFavorite = 20; int timesBookmark = 30; + int timesSolved = 40; challengeDoc1 = new ChallengeDocument(challengeRandomId1, title, level, localDateTime, detail, - Set.of(languageDoc1, languageDoc2), List.of(solutionsRandomId), topic, timesFavorite, timesBookmark, tags); + Set.of(languageDoc1, languageDoc2), List.of(solutionsRandomId), topic, timesFavorite, timesBookmark, timesSolved, tags); challengeDoc2 = new ChallengeDocument(challengeRandomId2, title, level, localDateTime, detail, - Set.of(languageDoc1, languageDoc2), List.of(solutionsRandomId), topic, timesFavorite, timesBookmark, tags); + Set.of(languageDoc1, languageDoc2), List.of(solutionsRandomId), topic, timesFavorite, timesBookmark, timesSolved, tags); challengeDto1 = getChallengeDtoMocked(challengeRandomId1, title, level, creationDate, detail, Set.of(languageDto1, languageDto2), diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/integration/ChallengeIntegrationTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/integration/ChallengeIntegrationTest.java index 234f8c008..2ce59d27a 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/integration/ChallengeIntegrationTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/integration/ChallengeIntegrationTest.java @@ -83,10 +83,10 @@ public void setUp() { ChallengeDocument challenge = new ChallengeDocument (uuid_1, title1, "Level 1", LocalDateTime.now(), detail, languageSet, - solutionList, Topic.LISTS, 20, 5, tags); + solutionList, Topic.LISTS, 20, 5,2, tags); ChallengeDocument challenge2 = new ChallengeDocument (uuid_2, title2, "Level 2", LocalDateTime.now(), detail, languageSet, - solutionList, Topic.COMPONENTS, 20, 30, tags); + solutionList, Topic.COMPONENTS, 20, 30,2, tags); challengeRepository.saveAll(Flux.just(challenge, challenge2)).blockLast(); } diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ChallengeRepositoryTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ChallengeRepositoryTest.java index 9adcad577..b6be94c00 100755 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ChallengeRepositoryTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ChallengeRepositoryTest.java @@ -18,7 +18,6 @@ import java.time.Duration; import java.time.LocalDateTime; import java.util.*; -import java.util.Locale; import static org.junit.Assert.*; import static org.springframework.test.util.AssertionErrors.fail; @@ -66,6 +65,8 @@ public void setUp() { List favoritedByList = List.of(UUID.randomUUID(),UUID.randomUUID()); + List solvedByList = List.of(UUID.randomUUID(),UUID.randomUUID()); + DetailDocument detail = new DetailDocument(description); String title1 = "Loops"; @@ -74,13 +75,13 @@ public void setUp() { ChallengeDocument challenge = new ChallengeDocument (uuid_1, title1, "MEDIUM", LocalDateTime.now(), detail, languageSet, solutionList, - Topic.DEBUGGING, 5, 3, tags); + Topic.DEBUGGING, 5, 3, 4, tags); ChallengeDocument challenge2 = new ChallengeDocument (uuid_2, title2, "EASY", LocalDateTime.now(), detail, languageSet, solutionList, - Topic.LISTS, 10, 2, tags); + Topic.LISTS, 10, 2, 7, tags); ChallengeDocument challenge3 = new ChallengeDocument (uuid_3, title3, "HARD", LocalDateTime.now(), detail, languageSet3, solutionList, - Topic.COMPONENTS, 15, 1, tags); + Topic.COMPONENTS, 15, 1, 9, tags); challengeRepository.saveAll(Flux.just(challenge, challenge2, challenge3)).blockLast(); diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java index 8698aeea6..f5a3e7953 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java @@ -104,7 +104,7 @@ void setUp() { challengeDocument = new ChallengeDocument(challengeRandomId, title, level, localDateTime, detail, Set.of(languageDocument), List.of(solutionsRandomId), Topic.COMPONENTS, - 20, 30,tags); + 20, 30, 40, tags); challengeDto = getChallengeDtoMocked(challengeRandomId, title, level, creationDate, detail, Set.of(languageDto), @@ -1320,7 +1320,7 @@ void getChallengesByFilter_ValidParams_FiltersAppliedCorrectly() { Set.of(new LanguageDocument(languageId, "Language Name", "image.png")), List.of(UUID.randomUUID()), Topic.COMPONENTS, - 0, 0, tags + 0, 0, 0, tags ); when(challengeRepository.findAllByUuidNotNullExcludingTestingValues()) @@ -1374,7 +1374,7 @@ void getChallengesByFilter_NoLanguage_FilterAppliedCorrectly() { Set.of(new LanguageDocument(UUID.randomUUID(), "Other Language", "image.png")), List.of(UUID.randomUUID()), Topic.COMPONENTS, - 0, 0, tags + 0, 0, 0, tags ); when(challengeRepository.findAllByUuidNotNullExcludingTestingValues()) From b3d270de5ea1475902d7ef79a5856f3f3cb0ca86 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 10:09:47 +0200 Subject: [PATCH 09/63] Eliminados comentarios innecesarios --- .../challenge/repository/ResourceRepositoryTest.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java index 7d8a8e53f..b64a54095 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java @@ -252,10 +252,7 @@ void deleteAllResourcesTest() { .verifyComplete(); } - // Revisar + limpiar esto - // este test Verifica que el método findByChallengeIdsContaining - // del repositorio devuelve únicamente los recursos que contienen - // el challengeId especificado en su lista de retos asociados. + @Test void findByChallengeIdsContaining_WhenChallengeIdExists_ReturnsResources() { From cc1eaca8b58de4ae30f9eed2f9be4e200a12a149 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 13:06:02 +0200 Subject: [PATCH 10/63] =?UTF-8?q?feat:=20a=C3=B1adir=20controlador=20y=20t?= =?UTF-8?q?ests=20para=20los=20recursos=20del=20reto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ResourceController.java | 21 ++++++++ .../controller/ResourceControllerTest.java | 52 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java index 0ce54e738..d5450803f 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java @@ -6,13 +6,17 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.util.UUID; + @RestController @Validated @RequestMapping(value = "/itachallenge/api/v1/resource") @@ -41,5 +45,22 @@ public Mono> createNewResource(@RequestBody @Valid R return resourceService.createResource(resourceDto) .map(createdResource -> ResponseEntity.ok().body(createdResource)); } + + @GetMapping + @Operation( + summary = "Get resources by Challenge ID", + description = "Retrieve all resources associated with a specific challenge.", + responses = { + @ApiResponse(responseCode = "200", description = "Resources found", content = @Content(schema = @Schema(implementation = ResourceDto.class))), + @ApiResponse(responseCode = "400", description = "Invalid challenge ID"), + @ApiResponse(responseCode = "500", description = "Internal server error") + } + ) + public Flux getResourcesByChallengeId( + @RequestParam @NotNull UUID challengeId + ) { + log.info("Fetching resources for challenge ID: {}", challengeId); + return resourceService.getResourcesByChallengeId(challengeId); + } } diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java index eb50a4f21..72f4c3ca7 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java @@ -13,6 +13,7 @@ import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.reactive.server.WebTestClient; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import static org.junit.Assert.*; @@ -108,4 +109,55 @@ void createNewResource_MissingRequiredFields_ReturnsBadRequest() { } + + @Test + void getResourcesByChallengeId_ValidId_ReturnsResources() { + // 1. Datos de prueba + UUID challengeId = UUID.randomUUID(); + ResourceDto resource1 = ResourceDto.builder() + .resourceId(UUID.randomUUID()) + .title("Resource 1") + .description("Desc 1") + .url("https://example.com/1") + .topic(Topic.DEBUGGING) + .contentType(ResourceContentType.VIDEO) + .challengeIds(List.of(challengeId)) + .build(); + + ResourceDto resource2 = ResourceDto.builder() + .resourceId(UUID.randomUUID()) + .title("Resource 2") + .description("Desc 2") + .url("https://example.com/2") + .topic(Topic.COMPONENTS) + .contentType(ResourceContentType.BLOG) + .challengeIds(List.of(challengeId)) + .build(); + + // 2. Mock del servicio + when(resourceService.getResourcesByChallengeId(challengeId)) + .thenReturn(Flux.just(resource1, resource2)); + + // 3. Ejecutar y verificar la petición HTTP + webTestClient.get() + .uri(uriBuilder -> uriBuilder + .path("/itachallenge/api/v1/resource") + .queryParam("challengeId", challengeId.toString()) + .build()) + .exchange() + .expectStatus().isOk() + .expectBodyList(ResourceDto.class) + .hasSize(2) + .value(resources -> { + assertEquals(resource1.getTitle(), resources.get(0).getTitle()); + assertEquals(resource2.getUrl(), resources.get(1).getUrl()); + }); + + // 4. Verificar interacción con el servicio + verify(resourceService, times(1)).getResourcesByChallengeId(challengeId); + } + + + + } From bba8eae10ef4414cb61fca2b275a2c1b2864aa85 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 13:12:18 +0200 Subject: [PATCH 11/63] =?UTF-8?q?faltaba=20a=C3=B1adir=20la=20ruta=20del?= =?UTF-8?q?=20get?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itachallenge/challenge/controller/ResourceController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java index d5450803f..9e7c51e24 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java @@ -46,7 +46,7 @@ public Mono> createNewResource(@RequestBody @Valid R .map(createdResource -> ResponseEntity.ok().body(createdResource)); } - @GetMapping + @GetMapping("/challenge/resources") @Operation( summary = "Get resources by Challenge ID", description = "Retrieve all resources associated with a specific challenge.", From 049bc0a1dc44998efd64da54adacc43d88cf99c9 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 13:14:33 +0200 Subject: [PATCH 12/63] =?UTF-8?q?correcci=C3=B3n=20de=20nomenclatura=20de?= =?UTF-8?q?=20ruta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itachallenge/challenge/controller/ResourceController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java index 9e7c51e24..c5996c0a6 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java @@ -46,7 +46,7 @@ public Mono> createNewResource(@RequestBody @Valid R .map(createdResource -> ResponseEntity.ok().body(createdResource)); } - @GetMapping("/challenge/resources") + @GetMapping("/resources") @Operation( summary = "Get resources by Challenge ID", description = "Retrieve all resources associated with a specific challenge.", From 16a9ec068f40b08a303ae152630551af1b0b0c52 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 13:37:17 +0200 Subject: [PATCH 13/63] Fix: updated ResourceControllerTest to match updated endpoint path --- .../challenge/controller/ResourceControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java index 72f4c3ca7..27fe6e469 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java @@ -141,7 +141,7 @@ void getResourcesByChallengeId_ValidId_ReturnsResources() { // 3. Ejecutar y verificar la petición HTTP webTestClient.get() .uri(uriBuilder -> uriBuilder - .path("/itachallenge/api/v1/resource") + .path("/itachallenge/api/v1/resource/resources") .queryParam("challengeId", challengeId.toString()) .build()) .exchange() From 102278b5fc58320867295d3eacd9f8c69909e2dd Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 13:42:08 +0200 Subject: [PATCH 14/63] Added endpoint to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1efe45e65..539af7f1f 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +* Issue #852: Added GET endpoint for retrieving resources from a challenge * Issue #829: Added bookmark POST endpoint in the Challenge Microservice to bookmark a challenge. * Issue #827: Added bookmark POST endpoint in the User Microservice to bookmark a challenge. * Issue #831: Added DELETE endpoint for user's bookmals in User Microservice. From 6d84c7e12b24ca0d40aa69e10e944b48a9cca163 Mon Sep 17 00:00:00 2001 From: Marc Bernabeu Rodriguez Date: Tue, 29 Apr 2025 13:53:34 +0200 Subject: [PATCH 15/63] Add MarcBernabeuRodriguez to contributors.yml --- contributors.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contributors.md b/contributors.md index fc25cea11..645fdd91c 100755 --- a/contributors.md +++ b/contributors.md @@ -82,4 +82,5 @@ * Albert Marín Miranda - https://github.com/Almami679 * Alexandra Bonet - https://github.com/AlexandraBonetCanela * Gwénaël Le Moing - https://github.com/g-lemoing -* Matías Meza - https://github.com/RustyGearBox \ No newline at end of file +* Matías Meza - https://github.com/RustyGearBox +* Marc Bernabeu Rodriguez - https://github.com/trisk910 \ No newline at end of file From e779a68626e9649ea76405be311cc4b12fae8e6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Meza?= Date: Tue, 29 Apr 2025 17:35:39 +0200 Subject: [PATCH 16/63] fix test method getChallengeDtoMocked --- .../challenge/helper/ChallengeDocumentToDtoConverterTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/helper/ChallengeDocumentToDtoConverterTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/helper/ChallengeDocumentToDtoConverterTest.java index 7ac1b4fa2..e6e1e87b8 100755 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/helper/ChallengeDocumentToDtoConverterTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/helper/ChallengeDocumentToDtoConverterTest.java @@ -126,6 +126,7 @@ private ChallengeDto getChallengeDtoMocked(UUID challengeId, String title, Strin when(challengeDocMocked.getTopic()).thenReturn(Topic.DEBUGGING); when(challengeDocMocked.getTimesFavorite()).thenReturn(20); when(challengeDocMocked.getTimesBookmark()).thenReturn(30); + when(challengeDocMocked.getTimesSolved()).thenReturn(40); when(challengeDocMocked.getTags()).thenReturn(tags); return challengeDocMocked; } From 5f25931b2e67f67d6a784e7cfe3bfbfddf1b8d81 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Tue, 29 Apr 2025 23:53:01 +0200 Subject: [PATCH 17/63] added SolvedDtoTest --- .../challenge/dto/SolvedDtoTest.java | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 itachallenge-challenge/src/test/java/com/itachallenge/challenge/dto/SolvedDtoTest.java diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/dto/SolvedDtoTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/dto/SolvedDtoTest.java new file mode 100644 index 000000000..dcdc73d52 --- /dev/null +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/dto/SolvedDtoTest.java @@ -0,0 +1,46 @@ +package com.itachallenge.challenge.dto; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class SolvedDtoTest { + + @Test + void testNoArgsConstructorAndSetters() { + SolvedDto dto = new SolvedDto(); + dto.setSolved(true); + dto.setTimesSolved(5); + + assertTrue(dto.isSolved()); + assertEquals(5, dto.getTimesSolved()); + } + + @Test + void testAllArgsConstructor() { + SolvedDto dto = new SolvedDto(false, 3); + + assertFalse(dto.isSolved()); + assertEquals(3, dto.getTimesSolved()); + } + + @Test + void testEqualsAndHashCode() { + SolvedDto dto1 = new SolvedDto(true, 2); + SolvedDto dto2 = new SolvedDto(true, 2); + SolvedDto dto3 = new SolvedDto(false, 5); + + assertEquals(dto1, dto2); + assertEquals(dto1.hashCode(), dto2.hashCode()); + assertNotEquals(dto1, dto3); + } + + @Test + void testToString() { + SolvedDto dto = new SolvedDto(true, 7); + String result = dto.toString(); + + assertTrue(result.contains("isSolved=true")); + assertTrue(result.contains("timesSolved=7")); + } +} From 4b1ec136feb36b64a34225d92be3e46aeb29fa86 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Wed, 30 Apr 2025 00:08:34 +0200 Subject: [PATCH 18/63] added tests for counterSolved --- .../challenge/document/ChallengeTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java index 60e8d9c77..78a3dc2e6 100755 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java @@ -206,6 +206,36 @@ void getTagsTest() { assertEquals(1, challenge.getTags().size()); } + @Test + void increaseTimesSolved_whenTimesSolvedIsNull_shouldSetToOne() { + // Given + ChallengeDocument challenge = ChallengeDocument.builder() + .uuid(UUID.randomUUID()) + .timesSolved(null) + .build(); + + // When + challenge.increaseTimesSolved(); + + // Then + assertEquals(1, challenge.getTimesSolved()); + } + + @Test + void increaseTimesSolved_whenTimesSolvedIsNonNull_shouldIncrementByOne() { + // Given + ChallengeDocument challenge = ChallengeDocument.builder() + .uuid(UUID.randomUUID()) + .timesSolved(3) + .build(); + + // When + challenge.increaseTimesSolved(); + + // Then + assertEquals(4, challenge.getTimesSolved()); + } + @Test void setTagsTest() { From d6466ff1821757be3dae1494f7203aa3228f9513 Mon Sep 17 00:00:00 2001 From: Almami679 Date: Wed, 30 Apr 2025 09:45:20 +0200 Subject: [PATCH 19/63] update determinateChallengeStatus() method --- .../user/document/enums/ChallengeStatus.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java b/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java index 671fed6f4..2594d1dd7 100644 --- a/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java +++ b/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java @@ -13,8 +13,13 @@ public enum ChallengeStatus {ENDED("ENDED"), } public static ChallengeStatus determineChallengeStatus(String status){ - return status != null && status.equalsIgnoreCase(ChallengeStatus.ENDED.getValue()) ? - ChallengeStatus.ENDED : null; + ChallengeStatus output = null; + if(status != null && status.equalsIgnoreCase(ChallengeStatus.ENDED.getValue())){ + output = ChallengeStatus.ENDED; + } else if (status != null && status.equalsIgnoreCase(ChallengeStatus.IN_PROGRESS.getValue())) { + output = ChallengeStatus.IN_PROGRESS; + } + return output; } } From d92224078d3ae1b62c9cdf65f58b3381e5602eb4 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Wed, 30 Apr 2025 10:33:27 +0200 Subject: [PATCH 20/63] deleted comments ChallangeTest --- .../com/itachallenge/challenge/document/ChallengeTest.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java index 78a3dc2e6..9bed12d69 100755 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/document/ChallengeTest.java @@ -208,31 +208,25 @@ void getTagsTest() { @Test void increaseTimesSolved_whenTimesSolvedIsNull_shouldSetToOne() { - // Given ChallengeDocument challenge = ChallengeDocument.builder() .uuid(UUID.randomUUID()) .timesSolved(null) .build(); - // When challenge.increaseTimesSolved(); - // Then assertEquals(1, challenge.getTimesSolved()); } @Test void increaseTimesSolved_whenTimesSolvedIsNonNull_shouldIncrementByOne() { - // Given ChallengeDocument challenge = ChallengeDocument.builder() .uuid(UUID.randomUUID()) .timesSolved(3) .build(); - // When challenge.increaseTimesSolved(); - // Then assertEquals(4, challenge.getTimesSolved()); } From ce2112b4eb8bb41f18ba96dc699ccac54ba778ff Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Wed, 30 Apr 2025 10:35:57 +0200 Subject: [PATCH 21/63] added UserChallengeActionType value to CHANGELOG --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb5f22db2..30325514e 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,8 @@ ## CHANGELOG The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +* Issue #844: Added value SOLVED to enum UserChallengeActionType * Issue #843: Added DELETE endpoint in the Challenge Microservice to unbookmark a challenge. * Issue #829: Added bookmark POST endpoint in the Challenge Microservice to bookmark a challenge. * Issue #827: Added bookmark POST endpoint in the User Microservice to bookmark a challenge. From 7c45bc551b03fbee17b2f1c83e651b6ca97ebc32 Mon Sep 17 00:00:00 2001 From: Santiago Hernandez Date: Wed, 30 Apr 2025 12:39:54 +0200 Subject: [PATCH 22/63] Add Santiago Hernandez to contributors list --- contributors.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contributors.md b/contributors.md index fc25cea11..800a96dde 100755 --- a/contributors.md +++ b/contributors.md @@ -82,4 +82,5 @@ * Albert Marín Miranda - https://github.com/Almami679 * Alexandra Bonet - https://github.com/AlexandraBonetCanela * Gwénaël Le Moing - https://github.com/g-lemoing -* Matías Meza - https://github.com/RustyGearBox \ No newline at end of file +* Matías Meza - https://github.com/RustyGearBox +* Santiago Hernandez Beltran - https://github.com/shernandez334 \ No newline at end of file From cd1b3a2dc56538af516b3837d6c6471a26ca48cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Meza?= Date: Wed, 30 Apr 2025 13:01:27 +0200 Subject: [PATCH 23/63] deleted #844 enum modif --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 30325514e..c3fda7236 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -* Issue #844: Added value SOLVED to enum UserChallengeActionType * Issue #843: Added DELETE endpoint in the Challenge Microservice to unbookmark a challenge. * Issue #829: Added bookmark POST endpoint in the Challenge Microservice to bookmark a challenge. * Issue #827: Added bookmark POST endpoint in the User Microservice to bookmark a challenge. From 3654dca159f68061d4e542a028e0321db815f2ed Mon Sep 17 00:00:00 2001 From: Almami679 Date: Wed, 30 Apr 2025 13:24:19 +0200 Subject: [PATCH 24/63] Ivan's Changues applied --- .../user/document/enums/ChallengeStatus.java | 13 ++++++++----- .../user/service/UserSolutionServiceImpl.java | 3 +-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java b/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java index 2594d1dd7..344f9549d 100644 --- a/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java +++ b/itachallenge-user/src/main/java/com/itachallenge/user/document/enums/ChallengeStatus.java @@ -2,6 +2,8 @@ import lombok.Getter; +import java.util.Arrays; + @Getter public enum ChallengeStatus {ENDED("ENDED"), IN_PROGRESS("IN_PROGRESS"); @@ -12,12 +14,13 @@ public enum ChallengeStatus {ENDED("ENDED"), this.value = value; } - public static ChallengeStatus determineChallengeStatus(String status){ + public static ChallengeStatus challengeStatusFromString(String status) { ChallengeStatus output = null; - if(status != null && status.equalsIgnoreCase(ChallengeStatus.ENDED.getValue())){ - output = ChallengeStatus.ENDED; - } else if (status != null && status.equalsIgnoreCase(ChallengeStatus.IN_PROGRESS.getValue())) { - output = ChallengeStatus.IN_PROGRESS; + if (status != null) { + output = Arrays.stream(ChallengeStatus.values()) + .filter(s -> status.equalsIgnoreCase(s.getValue())) + .findFirst() + .orElse(null); } return output; } diff --git a/itachallenge-user/src/main/java/com/itachallenge/user/service/UserSolutionServiceImpl.java b/itachallenge-user/src/main/java/com/itachallenge/user/service/UserSolutionServiceImpl.java index 2a5ab0795..e0970ab52 100644 --- a/itachallenge-user/src/main/java/com/itachallenge/user/service/UserSolutionServiceImpl.java +++ b/itachallenge-user/src/main/java/com/itachallenge/user/service/UserSolutionServiceImpl.java @@ -11,7 +11,6 @@ import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; -import java.util.List; import java.util.UUID; @Service @@ -37,7 +36,7 @@ public Mono addSolution(UserSolutionRequestDto userSolu .solutionText(userSolutionDto.getSolutionText()) .build(); - challengeStatus = ChallengeStatus.determineChallengeStatus(status); + challengeStatus = ChallengeStatus.challengeStatusFromString(status); if (challengeStatus == null) { log.error("PUT operation failed due to invalid challenge status parameter"); From 3b12851380d1beca0574d9311602df8cac48c4bb Mon Sep 17 00:00:00 2001 From: Alex Bonet Canela Date: Mon, 28 Apr 2025 09:08:11 +0200 Subject: [PATCH 25/63] added get all bookmarks from user endpoint --- .../user/controller/UserController.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/itachallenge-user/src/main/java/com/itachallenge/user/controller/UserController.java b/itachallenge-user/src/main/java/com/itachallenge/user/controller/UserController.java index c4380d5fe..2da6a70e3 100755 --- a/itachallenge-user/src/main/java/com/itachallenge/user/controller/UserController.java +++ b/itachallenge-user/src/main/java/com/itachallenge/user/controller/UserController.java @@ -370,4 +370,32 @@ public Mono>> getUserFavorites(@PathVariable String use return ResponseEntity.ok().body(favorites); }); } + + @Operation( + summary = "Gets challenges marked as bookmarks by a user", + description = "Returns a set of challenge IDs that the specified user has marked as bookmarked", + parameters = { + @Parameter( + name = "userId", + description = "UUID of the user", + required = true, + in = ParameterIn.PATH + ) + }, + responses = { + @ApiResponse(responseCode = "200", description = "Set of bookmarked challengeIds by user"), + @ApiResponse(responseCode = "404", description = "User not found"), + @ApiResponse(responseCode = "400", description = "The provided IDs are not valid."), + @ApiResponse(responseCode = "500", description = "Unexpected error") + } + ) + + @GetMapping("/users/{userId}/bookmarks") + public Mono>> getUserBookmarks(@PathVariable String userId) { + return userService.getUserBookmarks(userId) + .map(bookmarks -> { + log.info("Retrieved {} bookmarked challenges for user {}", bookmarks.size(), userId); + return ResponseEntity.ok().body(bookmarks); + }); + } } \ No newline at end of file From 41b3ca05ba03a9b3074996a02fe46838cef1e3f2 Mon Sep 17 00:00:00 2001 From: Alex Bonet Canela Date: Mon, 28 Apr 2025 09:08:44 +0200 Subject: [PATCH 26/63] added get all bookmarks Service method --- .../com/itachallenge/user/service/UserService.java | 2 ++ .../com/itachallenge/user/service/UserServiceImpl.java | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/itachallenge-user/src/main/java/com/itachallenge/user/service/UserService.java b/itachallenge-user/src/main/java/com/itachallenge/user/service/UserService.java index 51f36bcc3..a3c2bd2ac 100644 --- a/itachallenge-user/src/main/java/com/itachallenge/user/service/UserService.java +++ b/itachallenge-user/src/main/java/com/itachallenge/user/service/UserService.java @@ -18,4 +18,6 @@ public interface UserService { Mono> getUserFavorites(String userId); Mono deleteChallengeFromBookmarks(String userId, String challengeId); + + Mono> getUserBookmarks(String userId); } \ No newline at end of file diff --git a/itachallenge-user/src/main/java/com/itachallenge/user/service/UserServiceImpl.java b/itachallenge-user/src/main/java/com/itachallenge/user/service/UserServiceImpl.java index 97228e9d0..5a84c478e 100644 --- a/itachallenge-user/src/main/java/com/itachallenge/user/service/UserServiceImpl.java +++ b/itachallenge-user/src/main/java/com/itachallenge/user/service/UserServiceImpl.java @@ -159,4 +159,14 @@ public Mono> getUserFavorites(String userId) { ); } + @Override + public Mono> getUserBookmarks(String userId) { + return parseAndValidateUUID(userId) + .flatMap(userUuid -> + userRepository.findById(userUuid) + .switchIfEmpty(Mono.error(new NotFoundException("User not found with id: " + userId))) + .map(user -> Optional.ofNullable(user.getBookmarkChallenges()).orElseGet(HashSet::new)) + ); + } + } From caddfa6c60f956cae540dbd352c4d8086dbf0d4f Mon Sep 17 00:00:00 2001 From: Alex Bonet Canela Date: Mon, 28 Apr 2025 09:09:23 +0200 Subject: [PATCH 27/63] added Tests for get all bookmarks endpoint controller method --- .../user/controller/UserControllerTest.java | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/itachallenge-user/src/test/java/com/itachallenge/user/controller/UserControllerTest.java b/itachallenge-user/src/test/java/com/itachallenge/user/controller/UserControllerTest.java index 5b58a6cf5..77cbe840d 100644 --- a/itachallenge-user/src/test/java/com/itachallenge/user/controller/UserControllerTest.java +++ b/itachallenge-user/src/test/java/com/itachallenge/user/controller/UserControllerTest.java @@ -498,4 +498,75 @@ void getUserFavorites_returns500IfUnexpectedError() { verify(userService, times(1)).getUserFavorites(userId.toString()); } + @Test + @DisplayName("GET /users/{userId}/bookmarks returns bookmarked challenges") + void getUserBookmarks_returnsBookmarks() { + UUID userId = UUID.randomUUID(); + Set expectedBookmarks = Set.of(UUID.randomUUID(), UUID.randomUUID()); + + when(userService.getUserBookmarks(userId.toString())).thenReturn(Mono.just(expectedBookmarks)); + + webTestClient.get() + .uri("/itachallenge/api/v1/user/users/{userId}/bookmarks", userId) + .exchange() + .expectStatus().isOk() + .expectBodyList(UUID.class) + .hasSize(expectedBookmarks.size()) + .contains(expectedBookmarks.toArray(new UUID[0])); + + verify(userService, times(1)).getUserBookmarks(userId.toString()); + } + + @Test + @DisplayName("GET /users/{userId}/bookmarks returns 404 if user not found") + void getUserBookmarks_returns404IfUserNotFound() { + UUID userId = UUID.randomUUID(); + + when(userService.getUserBookmarks(userId.toString())) + .thenReturn(Mono.error(new NotFoundException("User not found"))); + + webTestClient.get() + .uri("/itachallenge/api/v1/user/users/{userId}/bookmarks", userId) + .exchange() + .expectStatus().isNotFound() + .expectBody(String.class).isEqualTo("User not found"); + + verify(userService, times(1)).getUserBookmarks(userId.toString()); + } + + @Test + @DisplayName("GET /users/{userId}/bookmarks returns 400 if UUID is invalid") + void getUserBookmarks_returns400IfInvalidUUID() { + String invalidUserId = "invalid-uuid"; + + when(userService.getUserBookmarks(invalidUserId)) + .thenReturn(Mono.error(new BadUUIDException("The provided IDs are not valid."))); + + webTestClient.get() + .uri("/itachallenge/api/v1/user/users/{userId}/bookmarks", invalidUserId) + .exchange() + .expectStatus().isBadRequest() + .expectBody(String.class).isEqualTo("The provided IDs are not valid."); + + verify(userService, times(1)).getUserBookmarks(invalidUserId); + } + + @Test + @DisplayName("GET /users/{userId}/bookmarks returns 500 if there is an internal error") + void getUserBookmarks_returns500IfUnexpectedError() { + UUID userId = UUID.randomUUID(); + + when(userService.getUserBookmarks(userId.toString())) + .thenReturn(Mono.error(new RuntimeException("Unexpected error"))); + + webTestClient.get() + .uri("/itachallenge/api/v1/user/users/{userId}/bookmarks", userId) + .exchange() + .expectStatus().isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR) + .expectBody(String.class).isEqualTo("Unexpected error happened."); + + verify(userService, times(1)).getUserBookmarks(userId.toString()); + } + + } From 9f8536e41caf337ca58302d4fda7c5a01d06d5e7 Mon Sep 17 00:00:00 2001 From: Alex Bonet Canela Date: Mon, 28 Apr 2025 09:10:31 +0200 Subject: [PATCH 28/63] added tests for UserService getUserBookmarks method --- .../user/service/UserServiceImplTest.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/itachallenge-user/src/test/java/com/itachallenge/user/service/UserServiceImplTest.java b/itachallenge-user/src/test/java/com/itachallenge/user/service/UserServiceImplTest.java index 99c6b3c4a..dc2816dcb 100644 --- a/itachallenge-user/src/test/java/com/itachallenge/user/service/UserServiceImplTest.java +++ b/itachallenge-user/src/test/java/com/itachallenge/user/service/UserServiceImplTest.java @@ -727,4 +727,69 @@ void getUserFavorites_WhenInvalidUUID_ReturnsBadUUIDException() { .verify(); } + @Test + @DisplayName("getUserBookmarks returns bookmarked challenges when the user exists and has challenges") + void getUserBookmarks_WhenUserExistsWithBookmarks_ReturnsSet() { + UUID userId = UUID.randomUUID(); + UUID challengeId1 = UUID.randomUUID(); + UUID challengeId2 = UUID.randomUUID(); + + Set bookmarks = Set.of(challengeId1, challengeId2); + UserDocument user = new UserDocument(); + user.setUuid(userId); + user.setBookmarkChallenges(bookmarks); + + when(userRepository.findById(userId)).thenReturn(Mono.just(user)); + + userService.getUserBookmarks(userId.toString()) + .as(StepVerifier::create) + .expectNextMatches(result -> result.size() == 2 && result.contains(challengeId1)) + .verifyComplete(); + } + + @Test + @DisplayName("getUserBookmarks returns an empty set when the user has no challenges marked.") + void getUserBookmarks_WhenUserHasNoBookmarks_ReturnsEmptySet() { + UUID userId = UUID.randomUUID(); + + UserDocument user = new UserDocument(); + user.setUuid(userId); + user.setFavoriteChallenges(null); // explícitely null + + when(userRepository.findById(userId)).thenReturn(Mono.just(user)); + + userService.getUserBookmarks(userId.toString()) + .as(StepVerifier::create) + .expectNextMatches(Set::isEmpty) + .verifyComplete(); + } + + @Test + @DisplayName("getUserBookmarks returns NotFoundException error when the user does not exist") + void getUserBookmarks_WhenUserNotFound_ReturnsError() { + UUID userId = UUID.randomUUID(); + + when(userRepository.findById(userId)).thenReturn(Mono.empty()); + + userService.getUserBookmarks(userId.toString()) + .as(StepVerifier::create) + .expectErrorMatches(error -> + error instanceof NotFoundException && + error.getMessage().equals("User not found with id: " + userId)) + .verify(); + } + + @Test + @DisplayName("getUserBookmarks throws BadUUIDException when the UUID format is invalid") + void getUserBookmarks_WhenInvalidUUID_ReturnsBadUUIDException() { + String invalidUUID = "invalid-uuid"; + + userService.getUserBookmarks(invalidUUID) + .as(StepVerifier::create) + .expectErrorMatches(error -> + error instanceof BadUUIDException && + error.getMessage().equals("Invalid ID format")) + .verify(); + } + } From c04b05a125ffd1ec3bdaad4814540579ffd9e72f Mon Sep 17 00:00:00 2001 From: Alex Bonet Canela Date: Mon, 28 Apr 2025 09:16:14 +0200 Subject: [PATCH 29/63] added issue 849 GET all bookmarked challenges endpoint to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3fda7236..3e11e13cd 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +* Issue #849: Added GET endpoint to retrieve all User's bookmarked challenges. * Issue #843: Added DELETE endpoint in the Challenge Microservice to unbookmark a challenge. * Issue #829: Added bookmark POST endpoint in the Challenge Microservice to bookmark a challenge. * Issue #827: Added bookmark POST endpoint in the User Microservice to bookmark a challenge. From 4937e0dccc79200d67187f02d1f6c686430ba35d Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Thu, 1 May 2025 11:27:44 +0200 Subject: [PATCH 30/63] fix: service method improved with custom exceptions --- .../challenge/service/ResourceServiceImpl.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java index 25df1d9d4..c9caf94d7 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java @@ -5,6 +5,9 @@ import com.itachallenge.challenge.dto.ResourceDto; import com.itachallenge.challenge.enums.AssociationType; import com.itachallenge.challenge.enums.Topic; +import com.itachallenge.challenge.exception.BadRequestException; +import com.itachallenge.challenge.exception.InternalServerErrorException; +import com.itachallenge.challenge.exception.ResourceNotFoundException; import com.itachallenge.challenge.helper.DocumentToDtoConverter; import com.itachallenge.challenge.repository.ResourceRepository; import org.slf4j.Logger; @@ -141,15 +144,18 @@ private Mono saveResource(ResourceDto resourceDto) { public Flux getResourcesByChallengeId(UUID challengeId) { if (challengeId == null) { - return Flux.error(new IllegalArgumentException("Challenge ID cannot be null")); + throw new BadRequestException("Challenge ID cannot be null"); } return resourceRepository.findByChallengeIdsContaining(challengeId) .map(resourceDoc -> resourceConverter.convertDocumentToDto(resourceDoc, ResourceDto.class)) .onErrorResume(error -> { - log.error("Error fetching resources: {}", error.getMessage()); - return Flux.error(new RuntimeException("Server error while fetching resources")); + log.error("Error fetching resources for challenge ID {}: {}", challengeId, error.getMessage()); + if (error instanceof ResourceNotFoundException) { + return Flux.error(error); + } + throw new InternalServerErrorException("Failed to fetch resources for challenge ID: " + challengeId); }); } } \ No newline at end of file From 81f8b1fb8540ba700e2f31aa0d432618a87a26d9 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Thu, 1 May 2025 11:43:31 +0200 Subject: [PATCH 31/63] Added tests for exceptions and changed the method for a more reactive approach --- .../service/ResourceServiceImpl.java | 28 +++++++-------- .../service/ResourceServiceImplTest.java | 34 ++++++++++++++----- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java index c9caf94d7..69f4be8a8 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java @@ -142,20 +142,18 @@ private Mono saveResource(ResourceDto resourceDto) { public Flux getResourcesByChallengeId(UUID challengeId) { - - if (challengeId == null) { - throw new BadRequestException("Challenge ID cannot be null"); - } - - - return resourceRepository.findByChallengeIdsContaining(challengeId) - .map(resourceDoc -> resourceConverter.convertDocumentToDto(resourceDoc, ResourceDto.class)) - .onErrorResume(error -> { - log.error("Error fetching resources for challenge ID {}: {}", challengeId, error.getMessage()); - if (error instanceof ResourceNotFoundException) { - return Flux.error(error); - } - throw new InternalServerErrorException("Failed to fetch resources for challenge ID: " + challengeId); - }); + return Mono.justOrEmpty(challengeId) + .switchIfEmpty(Mono.error(new BadRequestException("Challenge ID cannot be null"))) + .flatMapMany(validChallengeId -> + resourceRepository.findByChallengeIdsContaining(validChallengeId) + .map(resourceDoc -> resourceConverter.convertDocumentToDto(resourceDoc, ResourceDto.class)) + .onErrorResume(error -> { + log.error("Error fetching resources for challenge ID {}: {}", validChallengeId, error.getMessage()); + if (error instanceof ResourceNotFoundException) { + return Flux.error(error); + } + return Flux.error(new InternalServerErrorException("Failed to fetch resources for challenge ID: " + validChallengeId)); + }) + ); } } \ No newline at end of file diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java index 0d1a84bc7..79d21ce00 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java @@ -7,6 +7,9 @@ import com.itachallenge.challenge.enums.AssociationType; import com.itachallenge.challenge.enums.ResourceContentType; import com.itachallenge.challenge.enums.Topic; +import com.itachallenge.challenge.exception.BadRequestException; +import com.itachallenge.challenge.exception.InternalServerErrorException; +import com.itachallenge.challenge.exception.ResourceNotFoundException; import com.itachallenge.challenge.helper.DocumentToDtoConverter; import com.itachallenge.challenge.repository.ResourceRepository; import org.junit.jupiter.api.Test; @@ -471,13 +474,11 @@ void getResourcesByChallengeId_WhenNoResourcesExist_ReturnsEmptyFlux() { .verifyComplete(); } - //ID Nulo @Test - void getResourcesByChallengeId_WhenIdIsNull_ThrowsIllegalArgumentException() { - + void getResourcesByChallengeId_WhenIdIsNull_ThrowsBadRequestException() { StepVerifier.create(resourceService.getResourcesByChallengeId(null)) .expectErrorMatches(ex -> - ex instanceof IllegalArgumentException && + ex instanceof BadRequestException && ex.getMessage().equals("Challenge ID cannot be null") ) .verify(); @@ -485,17 +486,32 @@ void getResourcesByChallengeId_WhenIdIsNull_ThrowsIllegalArgumentException() { //Error en el repo @Test - void getResourcesByChallengeId_WhenRepositoryFails_PropagatesError() { - + void getResourcesByChallengeId_WhenRepositoryFails_ThrowsInternalServerErrorException() { UUID challengeId = UUID.randomUUID(); when(resourceRepository.findByChallengeIdsContaining(challengeId)) .thenReturn(Flux.error(new RuntimeException("DB Connection Failed"))); - StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) .expectErrorMatches(ex -> - ex instanceof RuntimeException && - ex.getMessage().equals("Server error while fetching resources") + ex instanceof InternalServerErrorException && + ex.getMessage().equals("Failed to fetch resources for challenge ID: " + challengeId) + ) + .verify(); + + verify(resourceRepository, times(1)).findByChallengeIdsContaining(challengeId); + } + + @Test + void getResourcesByChallengeId_WhenResourceNotFound_PropagatesException() { + UUID challengeId = UUID.randomUUID(); + ResourceNotFoundException ex = new ResourceNotFoundException("Not found"); + when(resourceRepository.findByChallengeIdsContaining(challengeId)) + .thenReturn(Flux.error(ex)); + + StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) + .expectErrorMatches(thrownEx -> + thrownEx instanceof ResourceNotFoundException && + thrownEx.getMessage().equals("Not found") ) .verify(); } From 0eabba75a472c0257ea3180223a79374026038a7 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Sun, 4 May 2025 17:38:09 +0200 Subject: [PATCH 32/63] added method addChallengeToSolved to UserInterface --- .../java/com/itachallenge/challenge/service/IUserService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IUserService.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IUserService.java index 8e22cd1e3..240204577 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IUserService.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IUserService.java @@ -9,4 +9,6 @@ public interface IUserService { Mono removeChallengeFromFavorites(String userId, String challengeId); Mono removeChallengeFromBookmarks(String userId, String challengeId); + + Mono addChallengeToSolved(String userId, String challengeId); } From 33fb7dd132f7b2460cc1f52fc4cfda7a4497a5d2 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Sun, 4 May 2025 17:38:40 +0200 Subject: [PATCH 33/63] added method addChallengeToSolved to UserServiceImpl --- .../com/itachallenge/challenge/service/UserServiceImpl.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/UserServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/UserServiceImpl.java index 7c8f24e7b..008d29ff9 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/UserServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/UserServiceImpl.java @@ -24,6 +24,7 @@ public class UserServiceImpl implements IUserService { private final String userServiceUrl; private final String X_FAVORITE_MESSAGE = "X-Favorite-Message"; private final String X_BOOKMARK_MESSAGE = "X-Bookmark-Message"; + private final String X_SOLVED_MESSAGE = "X-Solved-Message"; public UserServiceImpl( WebClient.Builder webClientBuilder, @@ -42,6 +43,11 @@ public Mono addChallengeToBookmarks(String userId, String challengeId) return callEndpoint(userId, challengeId, UserChallengeActionType.BOOKMARKS, X_BOOKMARK_MESSAGE, HttpMethod.POST); } + @Override + public Mono addChallengeToSolved(String userId, String challengeId) { + return callEndpoint(userId, challengeId, UserChallengeActionType.SOLVED, X_SOLVED_MESSAGE, HttpMethod.DELETE); + } + @Override public Mono removeChallengeFromFavorites(String userId, String challengeId) { return callEndpoint(userId, challengeId, UserChallengeActionType.FAVORITES, X_FAVORITE_MESSAGE, HttpMethod.DELETE); From 1f52646b35028a1e34960d58cf42617630ee950a Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Sun, 4 May 2025 17:38:57 +0200 Subject: [PATCH 34/63] added method addChallengeToSolved to ChallengeInterface --- .../com/itachallenge/challenge/service/IChallengeService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IChallengeService.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IChallengeService.java index 89965150f..fbafb906f 100755 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IChallengeService.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IChallengeService.java @@ -42,4 +42,6 @@ Flux> getChallengesByFilter(Optional idLa Mono removeChallengeFromFavorites(String challengeId, String userId); Mono removeChallengeFromBookmarks(String challengeId, String userId); + + Mono addChallengeToSolved(String challengeId, String userId); } From 5bfadc73083517d30ed2c92746e874c82e09e6c4 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Sun, 4 May 2025 17:39:06 +0200 Subject: [PATCH 35/63] added method addChallengeToSolved to ChallengeServiceImpl --- .../service/ChallengeServiceImpl.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java index 3acb8de3a..bcc48d855 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java @@ -448,5 +448,31 @@ public Mono removeChallengeFromBookmarks(String challengeId, String }); } + @Override + public Mono addChallengeToSolved(String challengeId, String userId) { + + Mono challengeIdMono = validateUUID(String.valueOf(challengeId)); + Mono userIdMono = validateUUID(String.valueOf(userId)); + + return Mono.zip(challengeIdMono, userIdMono) + .flatMap(Uuidtuple -> { + UUID challengeUuid = Uuidtuple.getT1(); + UUID userUuid = Uuidtuple.getT2(); + + return challengeRepository.findByUuid(challengeUuid) + .switchIfEmpty(Mono.error(new ChallengeNotFoundReturn404Exception(String.format(CHALLENGE_NOT_FOUND_ERROR, challengeUuid)))) + .flatMap(challenge -> userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString()) + .onErrorResume(throwable -> Mono.error(new InternalServerErrorException(throwable.getMessage()))) + .flatMap(isAddedToUsersSolved -> { + if (Boolean.TRUE.equals(isAddedToUsersSolved) || + Optional.ofNullable(challenge.getTimesSolved()).orElse(0) == 0) { + challenge.increaseTimesSolved(); + return challengeRepository.save(challenge); + } + return Mono.just(challenge); + }) + .map(savedChallenge -> new SolvedDto(true, savedChallenge.getTimesSolved()))); + }); + } } \ No newline at end of file From 24231eefdce2a90e257b202f58b064d68aec3be4 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Sun, 4 May 2025 17:39:20 +0200 Subject: [PATCH 36/63] added test for UserServiceImpl --- .../service/UserServiceImplTest.java | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/UserServiceImplTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/UserServiceImplTest.java index b3a76d4ee..b0d6a0f4e 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/UserServiceImplTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/UserServiceImplTest.java @@ -24,8 +24,10 @@ public class UserServiceImplTest { private static final String FAVORITES_URL = "/itachallenge/api/v1/user/users/%s/favorites/%s"; private static final String BOOKMARKS_URL = "/itachallenge/api/v1/user/users/%s/bookmarks/%s"; + private static final String SOLVED_URL = "/itachallenge/api/v1/user/users/%s/solved/%s"; public static final String X_FAVORITE_MESSAGE = "X-Favorite-Message"; public static final String X_BOOKMARK_MESSAGE = "X-Bookmark-Message"; + public static final String X_SOLVED_MESSAGE = "X-Solved-Message"; @BeforeEach void setUp() throws IOException { @@ -90,6 +92,29 @@ void addChallengeToBookmarks_AddedToUser_ReturnsTrue() throws InterruptedExcepti assertEquals("POST", request.getMethod()); } + @Test + void addChallengeToSolved_AddedToUser_ReturnsTrue() throws InterruptedException { + String userId = "someId"; + String challengeId = "anotherId"; + + mockWebServer.enqueue(new MockResponse() + .setBody("true") + .setResponseCode(201) + .addHeader("Content-Type", "application/json")); + + Mono result = userService.addChallengeToSolved(userId, challengeId); + + StepVerifier.create(result) + .expectNext(true) + .verifyComplete(); + + RecordedRequest request = mockWebServer.takeRequest(); + assertNotNull(request.getRequestUrl()); + assertEquals( + String.format(SOLVED_URL, userId, challengeId), + request.getRequestUrl().encodedPath()); + } + @Test void addChallengeToFavorites_NotAddedToUser_ReturnsFalse() throws InterruptedException { String userId = "someId"; @@ -137,6 +162,29 @@ void addChallengeToBookmarks_NotAddedToUser_ReturnsFalse() throws InterruptedExc assertEquals("POST", request.getMethod()); } + @Test + void addChallengeToSolved_NotAddedToUser_ReturnsFalse() throws InterruptedException { + String userId = "someId"; + String challengeId = "anotherId"; + + mockWebServer.enqueue(new MockResponse() + .setBody("false") + .setResponseCode(200) + .addHeader("Content-Type", "application/json")); + + Mono result = userService.addChallengeToSolved(userId, challengeId); + + StepVerifier.create(result) + .expectNext(false) + .verifyComplete(); + + RecordedRequest request = mockWebServer.takeRequest(); + assertNotNull(request.getRequestUrl()); + assertEquals( + String.format(SOLVED_URL, userId, challengeId), + request.getRequestUrl().encodedPath()); + } + @Test void addChallengeToFavorites_BadRequest_ReturnsError() throws InterruptedException { String userId = "someId"; @@ -194,6 +242,34 @@ void addChallengeToBookmarks_BadRequest_ReturnsError() throws InterruptedExcepti assertEquals("POST", request.getMethod()); } + @Test + void addChallengeToSolved_BadRequest_ReturnsError() throws InterruptedException { + String userId = "someId"; + String challengeId = "anotherId"; + String someErrorMessage = "Some error message"; + + mockWebServer.enqueue(new MockResponse() + .setBody("false") + .setResponseCode(400) + .addHeader(X_SOLVED_MESSAGE, someErrorMessage) + .addHeader("Content-Type", "application/json")); + + Mono result = userService.addChallengeToSolved(userId, challengeId); + + StepVerifier.create(result) + .expectErrorSatisfies(throwable -> { + assertInstanceOf(BadRequestException.class, throwable); + assertTrue(throwable.getMessage().contains(someErrorMessage)); + }) + .verify(); + + RecordedRequest request = mockWebServer.takeRequest(); + assertNotNull(request.getRequestUrl()); + assertEquals( + String.format(SOLVED_URL, userId, challengeId), + request.getRequestUrl().encodedPath()); + } + @Test void addChallengeToFavorites_UserNotFound_ReturnsError() throws InterruptedException { String userId = "someId"; @@ -247,6 +323,32 @@ void addChallengeToBookmarks_UserNotFound_ReturnsError() throws InterruptedExcep assertEquals("POST", request.getMethod()); } + @Test + void addChallengeToSolved_UserNotFound_ReturnsError() throws InterruptedException { + String userId = "someId"; + String challengeId = "anotherId"; + + mockWebServer.enqueue(new MockResponse() + .setBody("false") + .setResponseCode(404) + .addHeader("Content-Type", "application/json")); + + Mono result = userService.addChallengeToSolved(userId, challengeId); + + StepVerifier.create(result) + .expectErrorSatisfies(throwable -> { + assertInstanceOf(UserNotFoundException.class, throwable); + assertTrue(throwable.getMessage().contains("User not found")); + }) + .verify(); + + RecordedRequest request = mockWebServer.takeRequest(); + assertNotNull(request.getRequestUrl()); + assertEquals( + String.format(SOLVED_URL, userId, challengeId), + request.getRequestUrl().encodedPath()); + } + @Test void addChallengeToFavorites_500_ReturnsError() throws InterruptedException { String userId = "someId"; @@ -304,6 +406,34 @@ void addChallengeToBookmarks_500_ReturnsError() throws InterruptedException { assertEquals("POST", request.getMethod()); } + @Test + void addChallengeToSolved_500_ReturnsError() throws InterruptedException { + String userId = "someId"; + String challengeId = "anotherId"; + String someErrorMessage = "Some error message"; + + mockWebServer.enqueue(new MockResponse() + .setBody("false") + .setResponseCode(500) + .addHeader(X_SOLVED_MESSAGE, someErrorMessage) + .addHeader("Content-Type", "application/json")); + + Mono result = userService.addChallengeToSolved(userId, challengeId); + + StepVerifier.create(result) + .expectErrorSatisfies(throwable -> { + assertInstanceOf(InternalServerErrorException.class, throwable); + assertTrue(throwable.getMessage().contains(someErrorMessage)); + }) + .verify(); + + RecordedRequest request = mockWebServer.takeRequest(); + assertNotNull(request.getRequestUrl()); + assertEquals( + String.format(SOLVED_URL, userId, challengeId), + request.getRequestUrl().encodedPath()); + } + @Test void removeChallengeFromFavorites_DeletedFromUser_ReturnsTrue() throws InterruptedException { String userId = "someId"; From 7a8351401031a26e5833c072c357724416106908 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Sun, 4 May 2025 18:12:53 +0200 Subject: [PATCH 37/63] Refactorizado el manejo de ResourceNotFoundException en el Global Exception Handler y corregido la respuesta 404 en los controladores. --- .../controller/ResourceController.java | 31 +++---- .../exception/GlobalExceptionHandler.java | 2 +- .../service/ResourceServiceImpl.java | 1 + .../controller/ResourceControllerTest.java | 87 ++++++++++++------- .../service/ResourceServiceImplTest.java | 12 ++- 5 files changed, 79 insertions(+), 54 deletions(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java index c5996c0a6..4cce1fa78 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java @@ -1,17 +1,23 @@ package com.itachallenge.challenge.controller; import com.itachallenge.challenge.dto.ResourceDto; + import com.itachallenge.challenge.service.IResourceService; import io.swagger.v3.oas.annotations.Operation; + +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; -import jakarta.validation.constraints.NotNull; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; + import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -46,20 +52,15 @@ public Mono> createNewResource(@RequestBody @Valid R .map(createdResource -> ResponseEntity.ok().body(createdResource)); } - @GetMapping("/resources") - @Operation( - summary = "Get resources by Challenge ID", - description = "Retrieve all resources associated with a specific challenge.", - responses = { - @ApiResponse(responseCode = "200", description = "Resources found", content = @Content(schema = @Schema(implementation = ResourceDto.class))), - @ApiResponse(responseCode = "400", description = "Invalid challenge ID"), - @ApiResponse(responseCode = "500", description = "Internal server error") - } - ) - public Flux getResourcesByChallengeId( - @RequestParam @NotNull UUID challengeId - ) { - log.info("Fetching resources for challenge ID: {}", challengeId); + + @GetMapping("/challenge/{challengeId}") + @Operation(summary = "Get resources by challenge ID") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Resources found"), + @ApiResponse(responseCode = "400", description = "Invalid challenge ID"), + @ApiResponse(responseCode = "404", description = "No resources found") + }) + public Flux getResourcesByChallengeId(@PathVariable UUID challengeId) { return resourceService.getResourcesByChallengeId(challengeId); } } diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/exception/GlobalExceptionHandler.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/exception/GlobalExceptionHandler.java index f255484f6..3d55c4c65 100755 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/exception/GlobalExceptionHandler.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/exception/GlobalExceptionHandler.java @@ -62,7 +62,7 @@ public ResponseEntity handleChallengeNotFoundReturn404Exception(Chal @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity handleResourceNotFoundException(ResourceNotFoundException ex) { - return ResponseEntity.ok().body(new MessageDto(ex.getMessage())); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new MessageDto(ex.getMessage())); } @ExceptionHandler(LanguageNotFoundException.class) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java index 69f4be8a8..55b4bd81b 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java @@ -146,6 +146,7 @@ public Flux getResourcesByChallengeId(UUID challengeId) { .switchIfEmpty(Mono.error(new BadRequestException("Challenge ID cannot be null"))) .flatMapMany(validChallengeId -> resourceRepository.findByChallengeIdsContaining(validChallengeId) + .switchIfEmpty(Mono.error(new ResourceNotFoundException("No resources found for challenge ID: " + validChallengeId))) // Lanzar 404 si no se encuentra nada .map(resourceDoc -> resourceConverter.convertDocumentToDto(resourceDoc, ResourceDto.class)) .onErrorResume(error -> { log.error("Error fetching resources for challenge ID {}: {}", validChallengeId, error.getMessage()); diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java index 27fe6e469..dc520e216 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java @@ -1,9 +1,11 @@ package com.itachallenge.challenge.controller; + import com.itachallenge.challenge.dto.ResourceDto; import com.itachallenge.challenge.enums.AssociationType; import com.itachallenge.challenge.enums.ResourceContentType; import com.itachallenge.challenge.enums.Topic; +import com.itachallenge.challenge.exception.ResourceNotFoundException; import com.itachallenge.challenge.service.IResourceService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -24,6 +26,7 @@ import static org.mockito.ArgumentMatchers.any; +import java.lang.reflect.Constructor; import java.util.List; import java.util.UUID; @@ -108,56 +111,78 @@ void createNewResource_MissingRequiredFields_ReturnsBadRequest() { verify(resourceService, never()).createResource(any(ResourceDto.class)); } - - @Test void getResourcesByChallengeId_ValidId_ReturnsResources() { - // 1. Datos de prueba - UUID challengeId = UUID.randomUUID(); - ResourceDto resource1 = ResourceDto.builder() - .resourceId(UUID.randomUUID()) - .title("Resource 1") - .description("Desc 1") - .url("https://example.com/1") - .topic(Topic.DEBUGGING) - .contentType(ResourceContentType.VIDEO) - .challengeIds(List.of(challengeId)) - .build(); - ResourceDto resource2 = ResourceDto.builder() + UUID challengeId = UUID.randomUUID(); + ResourceDto mockResource = ResourceDto.builder() .resourceId(UUID.randomUUID()) - .title("Resource 2") - .description("Desc 2") - .url("https://example.com/2") + .title("Test Resource") + .description("Test Description") + .url("http://test.com") .topic(Topic.COMPONENTS) - .contentType(ResourceContentType.BLOG) + .contentType(ResourceContentType.VIDEO) .challengeIds(List.of(challengeId)) + .associationType(AssociationType.ALLSAMETOPIC) .build(); - // 2. Mock del servicio when(resourceService.getResourcesByChallengeId(challengeId)) - .thenReturn(Flux.just(resource1, resource2)); + .thenReturn(Flux.just(mockResource)); + - // 3. Ejecutar y verificar la petición HTTP webTestClient.get() - .uri(uriBuilder -> uriBuilder - .path("/itachallenge/api/v1/resource/resources") - .queryParam("challengeId", challengeId.toString()) - .build()) + .uri("/itachallenge/api/v1/resource/challenge/" + challengeId) // Ruta completa .exchange() .expectStatus().isOk() .expectBodyList(ResourceDto.class) - .hasSize(2) - .value(resources -> { - assertEquals(resource1.getTitle(), resources.get(0).getTitle()); - assertEquals(resource2.getUrl(), resources.get(1).getUrl()); - }); + .hasSize(1) + .contains(mockResource); - // 4. Verificar interacción con el servicio verify(resourceService, times(1)).getResourcesByChallengeId(challengeId); } + @Test + void getResourcesByChallengeId_InvalidId_ReturnsBadRequest() { + + webTestClient.get() + .uri("/itachallenge/api/v1/resource/challenge/null") // Ruta completa con ID inválido + .exchange() + .expectStatus().isBadRequest(); + verify(resourceService, never()).getResourcesByChallengeId(any()); + } + + @Test + void getResourcesByChallengeId_NoResources_ReturnsNotFoundWithMessage() { + + UUID challengeId = UUID.randomUUID(); + String errorMessage = "No resources found for challenge ID: " + challengeId; + + when(resourceService.getResourcesByChallengeId(challengeId)) + .thenReturn(Flux.error(new ResourceNotFoundException(errorMessage))); + + + webTestClient.get() + .uri("/itachallenge/api/v1/resource/challenge/" + challengeId) + .exchange() + .expectStatus().isNotFound() + .expectBody() + .jsonPath("$.message").isEqualTo(errorMessage); + + verify(resourceService, times(1)).getResourcesByChallengeId(challengeId); + } + } + + + + + + + + + + + diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java index 79d21ce00..dd366dd3a 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java @@ -460,18 +460,16 @@ void getResourcesByChallengeId_WhenResourcesExist_ReturnsFluxOfResources() { .verifyComplete(); } - // Valido SIN recursos @Test - void getResourcesByChallengeId_WhenNoResourcesExist_ReturnsEmptyFlux() { + void getResourcesByChallengeId_WhenNoResourcesExist_ThrowsResourceNotFoundException() { UUID challengeId = UUID.randomUUID(); when(resourceRepository.findByChallengeIdsContaining(challengeId)) - .thenReturn(Flux.empty()); - + .thenReturn(Flux.empty()); // Simula que no hay recursos StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) - .expectNextCount(0) - .verifyComplete(); + .expectError(ResourceNotFoundException.class) + .verify(); } @Test @@ -484,7 +482,7 @@ void getResourcesByChallengeId_WhenIdIsNull_ThrowsBadRequestException() { .verify(); } - //Error en el repo + @Test void getResourcesByChallengeId_WhenRepositoryFails_ThrowsInternalServerErrorException() { UUID challengeId = UUID.randomUUID(); From 22a04403a58de58738f55db803d48ac17b85a5fd Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Sun, 4 May 2025 18:14:34 +0200 Subject: [PATCH 38/63] added test for ChallengeServiceImpl --- .../service/ChallengeServiceImplTest.java | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java index a8aec4242..4283d8679 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java @@ -30,6 +30,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; import static org.assertj.core.api.Assertions.assertThat; @@ -551,6 +552,15 @@ void addChallengeToBookmarks_WhenChallengeUuidNotValid_ReturnsError() { .verify(); } + @Test + void addChallengeToSolved_WhenChallengeUuidNotValid_ReturnsError() { + StepVerifier.create(challengeService.addChallengeToSolved("InvalidUuid", UUID.randomUUID().toString())) + .expectErrorMatches(error -> + error instanceof BadUUIDException && + error.getMessage().equals("Invalid ID format. Please indicate the correct format.")) + .verify(); + } + @Test void addChallengeToFavorites_WhenUserUuidNotValid_ReturnsError() { StepVerifier.create(challengeService.addChallengeToFavorites(UUID.randomUUID().toString(), "InvalidUuid")) @@ -569,6 +579,15 @@ void addChallengeToBookmarks_WhenUserUuidNotValid_ReturnsError() { .verify(); } + @Test + void addChallengeToSolved_WhenUserUuidNotValid_ReturnsError() { + StepVerifier.create(challengeService.addChallengeToSolved(UUID.randomUUID().toString(), "InvalidUuid")) + .expectErrorMatches(error -> + error instanceof BadUUIDException && + error.getMessage().equals("Invalid ID format. Please indicate the correct format.")) + .verify(); + } + @Test void addChallengeToFavorites_WhenChallengeUuidIsNull_ReturnsError() { StepVerifier.create(challengeService.addChallengeToFavorites(null, UUID.randomUUID().toString())) @@ -587,6 +606,15 @@ void addChallengeToBookmarks_WhenChallengeUuidIsNull_ReturnsError() { .verify(); } + @Test + void addChallengeToSolved_WhenChallengeUuidIsNull_ReturnsError() { + StepVerifier.create(challengeService.addChallengeToSolved(null, UUID.randomUUID().toString())) + .expectErrorMatches(error -> + error instanceof BadUUIDException && + error.getMessage().equals("Invalid ID format. Please indicate the correct format.")) + .verify(); + } + @Test void addChallengeToFavorites_WhenUserUuidIsNull_ReturnsError() { StepVerifier.create(challengeService.addChallengeToFavorites(UUID.randomUUID().toString(), null)) @@ -605,6 +633,15 @@ void addChallengeToBookmarks_WhenUserUuidIsNull_ReturnsError() { .verify(); } + @Test + void addChallengeToSolved_WhenUserUuidIsNull_ReturnsError() { + StepVerifier.create(challengeService.addChallengeToSolved(UUID.randomUUID().toString(), null)) + .expectErrorMatches(error -> + error instanceof BadUUIDException && + error.getMessage().equals("Invalid ID format. Please indicate the correct format.")) + .verify(); + } + @Test void addChallengeToFavorites_WhenChallengeNotFound_ReturnsError() { String CHALLENGE_NOT_FOUND_ERROR = "Challenge with id: %s not found"; @@ -637,6 +674,22 @@ void addChallengeToBookmarks_WhenChallengeNotFound_ReturnsError() { verify(challengeRepository, times(1)).findByUuid(challengeUuid); } + @Test + void addChallengeToSolved_WhenChallengeNotFound_ReturnsError() { + String CHALLENGE_NOT_FOUND_ERROR = "Challenge with id: %s not found"; + UUID challengeUuid = UUID.randomUUID(); + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.empty()); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), UUID.randomUUID().toString())) + .expectErrorMatches(error -> + error instanceof ChallengeNotFoundReturn404Exception && + error.getMessage().equals(String.format(CHALLENGE_NOT_FOUND_ERROR, challengeUuid.toString()))) + .verify(); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + } + @Test void addChallengeToFavorites_WhenUserNotFound_ReturnsError() { UUID challengeUuid = UUID.randomUUID(); @@ -679,6 +732,27 @@ void addChallengeToBookmarks_WhenUserNotFound_ReturnsError() { verify(userService, times(1)).addChallengeToBookmarks(userUuid.toString(), challengeUuid.toString()); } + @Test + void addChallengeToSolved_WhenUserNotFound_ReturnsError() { + UUID challengeUuid = UUID.randomUUID(); + UUID userUuid = UUID.randomUUID(); + ChallengeDocument challenge = new ChallengeDocument(); + String message = "Some error message"; + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); + + when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.error(new UserNotFoundException(message))); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) + .expectErrorMatches(error -> + error instanceof InternalServerErrorException && + error.getMessage().equals(message)) + .verify(); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); + } + @Test void addChallengeToFavorites_WhenUserServiceReturnsCustomBadRequestException_ReturnsError() { UUID challengeUuid = UUID.randomUUID(); @@ -721,6 +795,27 @@ void addChallengeToBookmarks_WhenUserServiceReturnsCustomBadRequestException_Ret verify(userService, times(1)).addChallengeToBookmarks(userUuid.toString(), challengeUuid.toString()); } + @Test + void addChallengeToSolved_WhenUserServiceReturnsCustomBadRequestException_ReturnsError() { + UUID challengeUuid = UUID.randomUUID(); + UUID userUuid = UUID.randomUUID(); + ChallengeDocument challenge = new ChallengeDocument(); + String message = "Some error message"; + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); + + when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.error(new BadRequestException(message))); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) + .expectErrorMatches(error -> + error instanceof InternalServerErrorException && + error.getMessage().equals(message)) + .verify(); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); + } + @Test void addChallengeToFavorites_WhenUserServiceReturnsCustomInternalServerErrorException_ReturnsError() { UUID challengeUuid = UUID.randomUUID(); @@ -763,6 +858,27 @@ void addChallengeToBookmarks_WhenUserServiceReturnsCustomInternalServerErrorExce verify(userService, times(1)).addChallengeToBookmarks(userUuid.toString(), challengeUuid.toString()); } + @Test + void addChallengeToSolved_WhenUserServiceReturnsCustomInternalServerErrorException_ReturnsError() { + UUID challengeUuid = UUID.randomUUID(); + UUID userUuid = UUID.randomUUID(); + ChallengeDocument challenge = new ChallengeDocument(); + String message = "Some error message"; + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); + + when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.error(new InternalServerErrorException(message))); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) + .expectErrorMatches(error -> + error instanceof InternalServerErrorException && + error.getMessage().equals(message)) + .verify(); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); + } + @Test void addChallengeToFavorites_WhenUserServiceReturnsAnyException_ReturnsError() { UUID challengeUuid = UUID.randomUUID(); @@ -805,6 +921,27 @@ void addChallengeToBookmarks_WhenUserServiceReturnsAnyException_ReturnsError() { verify(userService, times(1)).addChallengeToBookmarks(userUuid.toString(), challengeUuid.toString()); } + @Test + void addChallengeToSolved_WhenUserServiceReturnsAnyException_ReturnsError() { + UUID challengeUuid = UUID.randomUUID(); + UUID userUuid = UUID.randomUUID(); + ChallengeDocument challenge = new ChallengeDocument(); + String message = "Some error message"; + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); + + when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.error(new Exception(message))); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) + .expectErrorMatches(error -> + error instanceof Exception && + error.getMessage().equals(message)) + .verify(); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); + } + @Test void addChallengeToFavorites_WhenAdded_IncreasesTimesFavoriteAndReturnsFavoriteDTO() { UUID challengeUuid = UUID.randomUUID(); @@ -859,6 +996,33 @@ void addChallengeToBookmarks_WhenAdded_IncreasesTimesBookmarkAndReturnsFavoriteD verify(challengeRepository, times(1)).save(challenge); } + @Test + void addChallengeToSolved_WhenAdded_IncreasesTimesSolvedAndReturnsSolvedDTO() { + UUID challengeUuid = UUID.randomUUID(); + UUID userUuid = UUID.randomUUID(); + ChallengeDocument challenge = new ChallengeDocument(); + int initialTimesSolved = 20; + + challenge.setTimesSolved(initialTimesSolved); + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); + when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.just(true)); + when(challengeRepository.save(challenge)).thenReturn(Mono.just(challenge)); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) + .expectNextMatches(solvedDto -> { + return solvedDto.getTimesSolved() == initialTimesSolved + 1 && + solvedDto.isSolved(); + }) + .verifyComplete(); + + Assertions.assertEquals(initialTimesSolved + 1, challenge.getTimesSolved()); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); + verify(challengeRepository, times(1)).save(challenge); + } + @Test void addChallengeToFavorites_WhenAddedAndInitialTimesFavoriteIsNull_IncreasesTimesFavoriteAndReturnsFavoriteDTO() { UUID challengeUuid = UUID.randomUUID(); @@ -911,6 +1075,32 @@ void addChallengeToBookmarks_WhenAddedAndInitialTimesFavoriteIsNull_IncreasesTim verify(challengeRepository, times(1)).save(challenge); } + @Test + void addChallengeToSolved_WhenAddedAndInitialTimesSolvedIsNull_IncreasesTimesSolvedAndReturnsSolvedDTO() { + UUID challengeUuid = UUID.randomUUID(); + UUID userUuid = UUID.randomUUID(); + ChallengeDocument challenge = new ChallengeDocument(); + + challenge.setTimesSolved(null); + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); + when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.just(true)); + when(challengeRepository.save(challenge)).thenReturn(Mono.just(challenge)); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) + .expectNextMatches(solvedDto -> { + return solvedDto.getTimesSolved() == 1 && + solvedDto.isSolved(); + }) + .verifyComplete(); + + Assertions.assertEquals(1, challenge.getTimesSolved()); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); + verify(challengeRepository, times(1)).save(challenge); + } + @Test void addChallengeToFavorites_WhenNotAdded_NotIncreaseTimesFavoriteAndReturnsFavoriteDTO() { UUID challengeUuid = UUID.randomUUID(); @@ -963,6 +1153,32 @@ void addChallengeToBookmarks_WhenNotAdded_NotIncreaseTimesBookmarkAndReturnsBook verify(challengeRepository, times(0)).save(any()); } + @Test + void addChallengeToSolved_WhenNotAdded_NotIncreaseTimesSolvedAndReturnsSolvedDTO() { + UUID challengeUuid = UUID.randomUUID(); + UUID userUuid = UUID.randomUUID(); + ChallengeDocument challenge = new ChallengeDocument(); + int initialTimesSolved = 20; + + challenge.setTimesSolved(initialTimesSolved); + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); + when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.just(false)); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) + .expectNextMatches(solvedDto -> { + return solvedDto.getTimesSolved() == initialTimesSolved && + solvedDto.isSolved(); + }) + .verifyComplete(); + + Assertions.assertEquals(initialTimesSolved, challenge.getTimesSolved()); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); + verify(challengeRepository, times(0)).save(any()); + } + @ParameterizedTest @MethodSource void addChallengeToFavorites_WhenNotAddedAndTimesFavoriteIsNullOrZero_SetTimesFavoriteToOneAndReturnsFavoriteDTO(Integer timesFavorite) { @@ -1017,6 +1233,33 @@ void addChallengeToBookmarks_WhenNotAddedAndTimesBookmarkIsNullOrZero_SetTimesBo verify(challengeRepository, times(1)).save(challenge); } + @ParameterizedTest + @MethodSource + void addChallengeToSolved_WhenNotAddedAndTimesSolvedIsNullOrZero_SetTimesSolvedToOneAndReturnsSolvedDTO(Integer timesSolved) { + UUID challengeUuid = UUID.randomUUID(); + UUID userUuid = UUID.randomUUID(); + ChallengeDocument challenge = new ChallengeDocument(); + + challenge.setTimesSolved(timesSolved); + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); + when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.just(false)); + when(challengeRepository.save(challenge)).thenReturn(Mono.just(challenge)); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) + .expectNextMatches(solvedDto -> { + return solvedDto.getTimesSolved() == 1 && + solvedDto.isSolved(); + }) + .verifyComplete(); + + Assertions.assertEquals(1, challenge.getTimesSolved()); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); + verify(challengeRepository, times(1)).save(challenge); + } + public static Stream addChallengeToFavorites_WhenNotAddedAndTimesFavoriteIsNullOrZero_SetTimesFavoriteToOneAndReturnsFavoriteDTO() { return Stream.of(null, 0); } @@ -1025,6 +1268,10 @@ public static Stream addChallengeToBookmarks_WhenNotAddedAndTimesBookma return Stream.of(null, 0); } + public static Stream addChallengeToSolved_WhenNotAddedAndTimesSolvedIsNullOrZero_SetTimesSolvedToOneAndReturnsSolvedDTO() { + return Stream.of(null, 0); + } + @Test void removeChallengeFromFavorites_WhenChallengeUuidNotValid_ReturnsError() { StepVerifier.create(challengeService.removeChallengeFromFavorites("InvalidUuid", UUID.randomUUID().toString())) From cac26906de31b6164d30f1404c7cdb63680f3a20 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Mon, 5 May 2025 11:36:38 +0200 Subject: [PATCH 39/63] applied requested changes --- .../challenge/service/ChallengeServiceImpl.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java index bcc48d855..5964746c9 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java @@ -451,20 +451,20 @@ public Mono removeChallengeFromBookmarks(String challengeId, String @Override public Mono addChallengeToSolved(String challengeId, String userId) { - Mono challengeIdMono = validateUUID(String.valueOf(challengeId)); - Mono userIdMono = validateUUID(String.valueOf(userId)); + Mono challengeIdMono = validateUUID(challengeId); + Mono userIdMono = validateUUID(userId); return Mono.zip(challengeIdMono, userIdMono) - .flatMap(Uuidtuple -> { - UUID challengeUuid = Uuidtuple.getT1(); - UUID userUuid = Uuidtuple.getT2(); + .flatMap(uuidTuple -> { + UUID challengeUuid = uuidTuple.getT1(); + UUID userUuid = uuidTuple.getT2(); return challengeRepository.findByUuid(challengeUuid) .switchIfEmpty(Mono.error(new ChallengeNotFoundReturn404Exception(String.format(CHALLENGE_NOT_FOUND_ERROR, challengeUuid)))) .flatMap(challenge -> userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString()) .onErrorResume(throwable -> Mono.error(new InternalServerErrorException(throwable.getMessage()))) - .flatMap(isAddedToUsersSolved -> { - if (Boolean.TRUE.equals(isAddedToUsersSolved) || + .flatMap(challengeWasAddedToUserSolvedList -> { + if (Boolean.TRUE.equals(challengeWasAddedToUserSolvedList) || Optional.ofNullable(challenge.getTimesSolved()).orElse(0) == 0) { challenge.increaseTimesSolved(); return challengeRepository.save(challenge); From 76ae09ca42e6174d3252727c42628b3d9bc87ef4 Mon Sep 17 00:00:00 2001 From: tonijimenez72 <156689808+tonijimenez72@users.noreply.github.com> Date: Wed, 30 Apr 2025 11:38:25 +0200 Subject: [PATCH 40/63] Add Toni Jimenez to contributors.md --- contributors.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contributors.md b/contributors.md index 645fdd91c..28d8ba9e7 100755 --- a/contributors.md +++ b/contributors.md @@ -83,4 +83,5 @@ * Alexandra Bonet - https://github.com/AlexandraBonetCanela * Gwénaël Le Moing - https://github.com/g-lemoing * Matías Meza - https://github.com/RustyGearBox -* Marc Bernabeu Rodriguez - https://github.com/trisk910 \ No newline at end of file +* Marc Bernabeu Rodriguez - https://github.com/trisk910 +* Toni Jiménez - https://github.com/tonijimenez72 \ No newline at end of file From 880b824fc0d7ee93eff09f8addac464a3365eb8b Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Thu, 10 Apr 2025 12:26:09 +0200 Subject: [PATCH 41/63] Delete of unnecesary Autowired - AuthService --- .../main/java/com/itachallenge/auth/service/AuthService.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/itachallenge-auth/src/main/java/com/itachallenge/auth/service/AuthService.java b/itachallenge-auth/src/main/java/com/itachallenge/auth/service/AuthService.java index 2a2f77694..fbe932443 100644 --- a/itachallenge-auth/src/main/java/com/itachallenge/auth/service/AuthService.java +++ b/itachallenge-auth/src/main/java/com/itachallenge/auth/service/AuthService.java @@ -5,7 +5,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; @@ -39,7 +38,6 @@ public class AuthService implements IAuthService { private final String clientSecret; - @Autowired public AuthService(WebClient.Builder webClientBuilder, @Value("${spring.security.oauth2.client.provider.github.token-uri}") String githubTokenUri, @Value("${spring.security.oauth2.client.provider.github.user-info-uri}") String githubUserInfoUri, From 79713f1a89943582e63baf9520d1a73af3a3a743 Mon Sep 17 00:00:00 2001 From: reiben815 Date: Wed, 30 Apr 2025 12:04:58 +0200 Subject: [PATCH 42/63] Add Enric Vicente to contributors.md --- contributors.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contributors.md b/contributors.md index 28d8ba9e7..1ef4680d8 100755 --- a/contributors.md +++ b/contributors.md @@ -84,4 +84,5 @@ * Gwénaël Le Moing - https://github.com/g-lemoing * Matías Meza - https://github.com/RustyGearBox * Marc Bernabeu Rodriguez - https://github.com/trisk910 -* Toni Jiménez - https://github.com/tonijimenez72 \ No newline at end of file +* Toni Jiménez - https://github.com/tonijimenez72 +* Enric Vicente - https://github.com/EnricW \ No newline at end of file From 4abc9e2d539c48403cb6d715f7c8bda65a77b91f Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Mon, 5 May 2025 12:55:45 +0200 Subject: [PATCH 43/63] fixed httpMethod addChallengeToSolved --- .../com/itachallenge/challenge/service/UserServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/UserServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/UserServiceImpl.java index 008d29ff9..69299a79f 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/UserServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/UserServiceImpl.java @@ -45,7 +45,7 @@ public Mono addChallengeToBookmarks(String userId, String challengeId) @Override public Mono addChallengeToSolved(String userId, String challengeId) { - return callEndpoint(userId, challengeId, UserChallengeActionType.SOLVED, X_SOLVED_MESSAGE, HttpMethod.DELETE); + return callEndpoint(userId, challengeId, UserChallengeActionType.SOLVED, X_SOLVED_MESSAGE, HttpMethod.POST); } @Override From 4c655dbba53ddfebf5e5783cef73f764115cea56 Mon Sep 17 00:00:00 2001 From: isma Date: Wed, 30 Apr 2025 12:32:42 +0200 Subject: [PATCH 44/63] =?UTF-8?q?Add=20Ismael=20Peir=C3=B3=20to=20contribu?= =?UTF-8?q?tors.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- contributors.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contributors.md b/contributors.md index 1ef4680d8..6bf628193 100755 --- a/contributors.md +++ b/contributors.md @@ -85,4 +85,5 @@ * Matías Meza - https://github.com/RustyGearBox * Marc Bernabeu Rodriguez - https://github.com/trisk910 * Toni Jiménez - https://github.com/tonijimenez72 -* Enric Vicente - https://github.com/EnricW \ No newline at end of file +* Enric Vicente - https://github.com/EnricW +* Ismael Peiró - https://github.com/IsmaPeiro \ No newline at end of file From 0051914d298aa4317f41d6e377f139d2643b52bc Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Mon, 5 May 2025 13:18:33 +0200 Subject: [PATCH 45/63] addChallengeToSolved mocked --- .../service/ChallengeServiceImpl.java | 54 +++++++++---------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java index 5964746c9..934d41ba1 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java @@ -1,12 +1,6 @@ package com.itachallenge.challenge.service; import com.itachallenge.challenge.document.*; -import com.itachallenge.challenge.dto.ChallengeDto; -import com.itachallenge.challenge.dto.GenericResultDto; -import com.itachallenge.challenge.dto.SolutionDto; -import com.itachallenge.challenge.document.ChallengeDocument; -import com.itachallenge.challenge.document.LanguageDocument; -import com.itachallenge.challenge.document.SolutionDocument; import com.itachallenge.challenge.dto.*; import com.itachallenge.challenge.enums.Topic; import com.itachallenge.challenge.exception.*; @@ -448,31 +442,33 @@ public Mono removeChallengeFromBookmarks(String challengeId, String }); } - @Override - public Mono addChallengeToSolved(String challengeId, String userId) { +@Override +public Mono addChallengeToSolved(String challengeId, String userId) { + Mono challengeIdMono = validateUUID(challengeId); + Mono userIdMono = validateUUID(userId); - Mono challengeIdMono = validateUUID(challengeId); - Mono userIdMono = validateUUID(userId); + return Mono.zip(challengeIdMono, userIdMono) + .flatMap(uuidTuple -> { + UUID challengeUuid = uuidTuple.getT1(); + UUID userUuid = uuidTuple.getT2(); - return Mono.zip(challengeIdMono, userIdMono) - .flatMap(uuidTuple -> { - UUID challengeUuid = uuidTuple.getT1(); - UUID userUuid = uuidTuple.getT2(); + return challengeRepository.findByUuid(challengeUuid) + .switchIfEmpty(Mono.error(new ChallengeNotFoundReturn404Exception( + String.format(CHALLENGE_NOT_FOUND_ERROR, challengeUuid)))) + .flatMap(challenge -> { + log.info("It should be connected to user service using the fields {} and {}", challengeUuid, userUuid); - return challengeRepository.findByUuid(challengeUuid) - .switchIfEmpty(Mono.error(new ChallengeNotFoundReturn404Exception(String.format(CHALLENGE_NOT_FOUND_ERROR, challengeUuid)))) - .flatMap(challenge -> userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString()) - .onErrorResume(throwable -> Mono.error(new InternalServerErrorException(throwable.getMessage()))) - .flatMap(challengeWasAddedToUserSolvedList -> { - if (Boolean.TRUE.equals(challengeWasAddedToUserSolvedList) || - Optional.ofNullable(challenge.getTimesSolved()).orElse(0) == 0) { - challenge.increaseTimesSolved(); - return challengeRepository.save(challenge); - } - return Mono.just(challenge); - }) - .map(savedChallenge -> new SolvedDto(true, savedChallenge.getTimesSolved()))); - }); - } + //The part of the user service is not implemented yet, it should be connected to the user service in the future. + + if (Optional.ofNullable(challenge.getTimesSolved()).orElse(0) == 0) { + challenge.increaseTimesSolved(); + return challengeRepository.save(challenge); + } + + return Mono.just(challenge); + }) + .map(updatedChallenge -> new SolvedDto(true, updatedChallenge.getTimesSolved())); + }); +} } \ No newline at end of file From e01e4c3b8894aecb61152ecfccccc2c0db0a2213 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Mon, 5 May 2025 13:21:23 +0200 Subject: [PATCH 46/63] fixed imports --- .../challenge/service/ChallengeServiceImpl.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java index 934d41ba1..1ae8b1cb7 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ChallengeServiceImpl.java @@ -1,6 +1,12 @@ package com.itachallenge.challenge.service; import com.itachallenge.challenge.document.*; +import com.itachallenge.challenge.dto.ChallengeDto; +import com.itachallenge.challenge.dto.GenericResultDto; +import com.itachallenge.challenge.dto.SolutionDto; +import com.itachallenge.challenge.document.ChallengeDocument; +import com.itachallenge.challenge.document.LanguageDocument; +import com.itachallenge.challenge.document.SolutionDocument; import com.itachallenge.challenge.dto.*; import com.itachallenge.challenge.enums.Topic; import com.itachallenge.challenge.exception.*; From 41c1db52d97bc59bb8ad24a5d44d35693929954e Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Mon, 5 May 2025 13:30:43 +0200 Subject: [PATCH 47/63] new tests method mocked --- .../service/ChallengeServiceImplTest.java | 262 +++++------------- 1 file changed, 72 insertions(+), 190 deletions(-) diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java index 4283d8679..18f981acc 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ChallengeServiceImplTest.java @@ -316,6 +316,78 @@ void testGetSolutions_ChallengeNotFound() { verify(solutionConverter, never()).convertDocumentFluxToDtoFlux(any(), any()); } +@Test +void addChallengeToSolved_WhenChallengeTimesSolvedIsNull_IncreasesTimesSolvedAndReturnsSolvedDTO() { + UUID challengeUuid = UUID.randomUUID(); + UUID userUuid = UUID.randomUUID(); + ChallengeDocument challenge = new ChallengeDocument(); + + challenge.setTimesSolved(null); + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); + when(challengeRepository.save(challenge)).thenReturn(Mono.just(challenge)); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) + .expectNextMatches(solvedDto -> { + return solvedDto.getTimesSolved() == 1 && + solvedDto.isSolved(); + }) + .verifyComplete(); + + Assertions.assertEquals(1, challenge.getTimesSolved()); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + verify(challengeRepository, times(1)).save(challenge); +} + +@Test +void addChallengeToSolved_WhenChallengeTimesSolvedIsZero_IncreasesTimesSolvedAndReturnsSolvedDTO() { + UUID challengeUuid = UUID.randomUUID(); + UUID userUuid = UUID.randomUUID(); + ChallengeDocument challenge = new ChallengeDocument(); + + challenge.setTimesSolved(0); + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); + when(challengeRepository.save(challenge)).thenReturn(Mono.just(challenge)); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) + .expectNextMatches(solvedDto -> { + return solvedDto.getTimesSolved() == 1 && + solvedDto.isSolved(); + }) + .verifyComplete(); + + Assertions.assertEquals(1, challenge.getTimesSolved()); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + verify(challengeRepository, times(1)).save(challenge); +} + +@Test +void addChallengeToSolved_WhenChallengeAlreadySolved_DoesNotIncreaseTimesSolvedAndReturnsSolvedDTO() { + UUID challengeUuid = UUID.randomUUID(); + UUID userUuid = UUID.randomUUID(); + ChallengeDocument challenge = new ChallengeDocument(); + int initialTimesSolved = 5; + + challenge.setTimesSolved(initialTimesSolved); + + when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); + + StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) + .expectNextMatches(solvedDto -> { + return solvedDto.getTimesSolved() == initialTimesSolved && + solvedDto.isSolved(); + }) + .verifyComplete(); + + Assertions.assertEquals(initialTimesSolved, challenge.getTimesSolved()); + + verify(challengeRepository, times(1)).findByUuid(challengeUuid); + verify(challengeRepository, times(0)).save(any()); +} + @Test void addSolution_ValidChallengeIdAndLanguageId_SolutionAdded() { // Arrange @@ -732,27 +804,6 @@ void addChallengeToBookmarks_WhenUserNotFound_ReturnsError() { verify(userService, times(1)).addChallengeToBookmarks(userUuid.toString(), challengeUuid.toString()); } - @Test - void addChallengeToSolved_WhenUserNotFound_ReturnsError() { - UUID challengeUuid = UUID.randomUUID(); - UUID userUuid = UUID.randomUUID(); - ChallengeDocument challenge = new ChallengeDocument(); - String message = "Some error message"; - - when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); - - when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.error(new UserNotFoundException(message))); - - StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) - .expectErrorMatches(error -> - error instanceof InternalServerErrorException && - error.getMessage().equals(message)) - .verify(); - - verify(challengeRepository, times(1)).findByUuid(challengeUuid); - verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); - } - @Test void addChallengeToFavorites_WhenUserServiceReturnsCustomBadRequestException_ReturnsError() { UUID challengeUuid = UUID.randomUUID(); @@ -795,27 +846,6 @@ void addChallengeToBookmarks_WhenUserServiceReturnsCustomBadRequestException_Ret verify(userService, times(1)).addChallengeToBookmarks(userUuid.toString(), challengeUuid.toString()); } - @Test - void addChallengeToSolved_WhenUserServiceReturnsCustomBadRequestException_ReturnsError() { - UUID challengeUuid = UUID.randomUUID(); - UUID userUuid = UUID.randomUUID(); - ChallengeDocument challenge = new ChallengeDocument(); - String message = "Some error message"; - - when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); - - when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.error(new BadRequestException(message))); - - StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) - .expectErrorMatches(error -> - error instanceof InternalServerErrorException && - error.getMessage().equals(message)) - .verify(); - - verify(challengeRepository, times(1)).findByUuid(challengeUuid); - verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); - } - @Test void addChallengeToFavorites_WhenUserServiceReturnsCustomInternalServerErrorException_ReturnsError() { UUID challengeUuid = UUID.randomUUID(); @@ -858,27 +888,6 @@ void addChallengeToBookmarks_WhenUserServiceReturnsCustomInternalServerErrorExce verify(userService, times(1)).addChallengeToBookmarks(userUuid.toString(), challengeUuid.toString()); } - @Test - void addChallengeToSolved_WhenUserServiceReturnsCustomInternalServerErrorException_ReturnsError() { - UUID challengeUuid = UUID.randomUUID(); - UUID userUuid = UUID.randomUUID(); - ChallengeDocument challenge = new ChallengeDocument(); - String message = "Some error message"; - - when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); - - when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.error(new InternalServerErrorException(message))); - - StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) - .expectErrorMatches(error -> - error instanceof InternalServerErrorException && - error.getMessage().equals(message)) - .verify(); - - verify(challengeRepository, times(1)).findByUuid(challengeUuid); - verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); - } - @Test void addChallengeToFavorites_WhenUserServiceReturnsAnyException_ReturnsError() { UUID challengeUuid = UUID.randomUUID(); @@ -921,27 +930,6 @@ void addChallengeToBookmarks_WhenUserServiceReturnsAnyException_ReturnsError() { verify(userService, times(1)).addChallengeToBookmarks(userUuid.toString(), challengeUuid.toString()); } - @Test - void addChallengeToSolved_WhenUserServiceReturnsAnyException_ReturnsError() { - UUID challengeUuid = UUID.randomUUID(); - UUID userUuid = UUID.randomUUID(); - ChallengeDocument challenge = new ChallengeDocument(); - String message = "Some error message"; - - when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); - - when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.error(new Exception(message))); - - StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) - .expectErrorMatches(error -> - error instanceof Exception && - error.getMessage().equals(message)) - .verify(); - - verify(challengeRepository, times(1)).findByUuid(challengeUuid); - verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); - } - @Test void addChallengeToFavorites_WhenAdded_IncreasesTimesFavoriteAndReturnsFavoriteDTO() { UUID challengeUuid = UUID.randomUUID(); @@ -996,33 +984,6 @@ void addChallengeToBookmarks_WhenAdded_IncreasesTimesBookmarkAndReturnsFavoriteD verify(challengeRepository, times(1)).save(challenge); } - @Test - void addChallengeToSolved_WhenAdded_IncreasesTimesSolvedAndReturnsSolvedDTO() { - UUID challengeUuid = UUID.randomUUID(); - UUID userUuid = UUID.randomUUID(); - ChallengeDocument challenge = new ChallengeDocument(); - int initialTimesSolved = 20; - - challenge.setTimesSolved(initialTimesSolved); - - when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); - when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.just(true)); - when(challengeRepository.save(challenge)).thenReturn(Mono.just(challenge)); - - StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) - .expectNextMatches(solvedDto -> { - return solvedDto.getTimesSolved() == initialTimesSolved + 1 && - solvedDto.isSolved(); - }) - .verifyComplete(); - - Assertions.assertEquals(initialTimesSolved + 1, challenge.getTimesSolved()); - - verify(challengeRepository, times(1)).findByUuid(challengeUuid); - verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); - verify(challengeRepository, times(1)).save(challenge); - } - @Test void addChallengeToFavorites_WhenAddedAndInitialTimesFavoriteIsNull_IncreasesTimesFavoriteAndReturnsFavoriteDTO() { UUID challengeUuid = UUID.randomUUID(); @@ -1075,32 +1036,6 @@ void addChallengeToBookmarks_WhenAddedAndInitialTimesFavoriteIsNull_IncreasesTim verify(challengeRepository, times(1)).save(challenge); } - @Test - void addChallengeToSolved_WhenAddedAndInitialTimesSolvedIsNull_IncreasesTimesSolvedAndReturnsSolvedDTO() { - UUID challengeUuid = UUID.randomUUID(); - UUID userUuid = UUID.randomUUID(); - ChallengeDocument challenge = new ChallengeDocument(); - - challenge.setTimesSolved(null); - - when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); - when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.just(true)); - when(challengeRepository.save(challenge)).thenReturn(Mono.just(challenge)); - - StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) - .expectNextMatches(solvedDto -> { - return solvedDto.getTimesSolved() == 1 && - solvedDto.isSolved(); - }) - .verifyComplete(); - - Assertions.assertEquals(1, challenge.getTimesSolved()); - - verify(challengeRepository, times(1)).findByUuid(challengeUuid); - verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); - verify(challengeRepository, times(1)).save(challenge); - } - @Test void addChallengeToFavorites_WhenNotAdded_NotIncreaseTimesFavoriteAndReturnsFavoriteDTO() { UUID challengeUuid = UUID.randomUUID(); @@ -1153,32 +1088,6 @@ void addChallengeToBookmarks_WhenNotAdded_NotIncreaseTimesBookmarkAndReturnsBook verify(challengeRepository, times(0)).save(any()); } - @Test - void addChallengeToSolved_WhenNotAdded_NotIncreaseTimesSolvedAndReturnsSolvedDTO() { - UUID challengeUuid = UUID.randomUUID(); - UUID userUuid = UUID.randomUUID(); - ChallengeDocument challenge = new ChallengeDocument(); - int initialTimesSolved = 20; - - challenge.setTimesSolved(initialTimesSolved); - - when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); - when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.just(false)); - - StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) - .expectNextMatches(solvedDto -> { - return solvedDto.getTimesSolved() == initialTimesSolved && - solvedDto.isSolved(); - }) - .verifyComplete(); - - Assertions.assertEquals(initialTimesSolved, challenge.getTimesSolved()); - - verify(challengeRepository, times(1)).findByUuid(challengeUuid); - verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); - verify(challengeRepository, times(0)).save(any()); - } - @ParameterizedTest @MethodSource void addChallengeToFavorites_WhenNotAddedAndTimesFavoriteIsNullOrZero_SetTimesFavoriteToOneAndReturnsFavoriteDTO(Integer timesFavorite) { @@ -1233,33 +1142,6 @@ void addChallengeToBookmarks_WhenNotAddedAndTimesBookmarkIsNullOrZero_SetTimesBo verify(challengeRepository, times(1)).save(challenge); } - @ParameterizedTest - @MethodSource - void addChallengeToSolved_WhenNotAddedAndTimesSolvedIsNullOrZero_SetTimesSolvedToOneAndReturnsSolvedDTO(Integer timesSolved) { - UUID challengeUuid = UUID.randomUUID(); - UUID userUuid = UUID.randomUUID(); - ChallengeDocument challenge = new ChallengeDocument(); - - challenge.setTimesSolved(timesSolved); - - when(challengeRepository.findByUuid(challengeUuid)).thenReturn(Mono.just(challenge)); - when(userService.addChallengeToSolved(userUuid.toString(), challengeUuid.toString())).thenReturn(Mono.just(false)); - when(challengeRepository.save(challenge)).thenReturn(Mono.just(challenge)); - - StepVerifier.create(challengeService.addChallengeToSolved(challengeUuid.toString(), userUuid.toString())) - .expectNextMatches(solvedDto -> { - return solvedDto.getTimesSolved() == 1 && - solvedDto.isSolved(); - }) - .verifyComplete(); - - Assertions.assertEquals(1, challenge.getTimesSolved()); - - verify(challengeRepository, times(1)).findByUuid(challengeUuid); - verify(userService, times(1)).addChallengeToSolved(userUuid.toString(), challengeUuid.toString()); - verify(challengeRepository, times(1)).save(challenge); - } - public static Stream addChallengeToFavorites_WhenNotAddedAndTimesFavoriteIsNullOrZero_SetTimesFavoriteToOneAndReturnsFavoriteDTO() { return Stream.of(null, 0); } From 429b27baf92b22e60bb196f1411534318aca30e2 Mon Sep 17 00:00:00 2001 From: shernandez334 <52896744+shernandez334@users.noreply.github.com> Date: Mon, 5 May 2025 13:39:43 +0200 Subject: [PATCH 48/63] Update contributors.md --- contributors.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contributors.md b/contributors.md index 7044fc0b5..8473599e7 100755 --- a/contributors.md +++ b/contributors.md @@ -86,6 +86,5 @@ * Marc Bernabeu Rodriguez - https://github.com/trisk910 * Toni Jiménez - https://github.com/tonijimenez72 * Enric Vicente - https://github.com/EnricW -* Santiago Hernandez Beltran - https://github.com/shernandez334 * Ismael Peiró - https://github.com/IsmaPeiro -* Santiago Hernandez Beltran - https://github.com/shernandez334 \ No newline at end of file +* Santiago Hernandez Beltran - https://github.com/shernandez334 From 2eb22b50b7f91f65957d07fdbbe11369ec84f058 Mon Sep 17 00:00:00 2001 From: shernandez334 <52896744+shernandez334@users.noreply.github.com> Date: Mon, 5 May 2025 13:40:06 +0200 Subject: [PATCH 49/63] Update contributors.md From aeb3557e55be1ae41085d72da7e1327f873a16ce Mon Sep 17 00:00:00 2001 From: IngaD89 Date: Wed, 30 Apr 2025 14:03:57 +0200 Subject: [PATCH 50/63] add inga to contributrs --- contributors.md | 1 + 1 file changed, 1 insertion(+) diff --git a/contributors.md b/contributors.md index 8473599e7..ad300ebf8 100755 --- a/contributors.md +++ b/contributors.md @@ -88,3 +88,4 @@ * Enric Vicente - https://github.com/EnricW * Ismael Peiró - https://github.com/IsmaPeiro * Santiago Hernandez Beltran - https://github.com/shernandez334 +* Inga Demetrashvili - https://github.com/IngaD89 \ No newline at end of file From c67033f0d9fdb8dacf4bbaf75a03afccda5e13ce Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Mon, 5 May 2025 14:08:24 +0200 Subject: [PATCH 51/63] added controller method --- .../controller/ChallengeSolvedController.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ChallengeSolvedController.java diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ChallengeSolvedController.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ChallengeSolvedController.java new file mode 100644 index 000000000..0403041ee --- /dev/null +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ChallengeSolvedController.java @@ -0,0 +1,53 @@ +package com.itachallenge.challenge.controller; + +import com.itachallenge.challenge.dto.SolvedDto; +import com.itachallenge.challenge.exception.BadRequestException; +import com.itachallenge.challenge.exception.JwtException; +import com.itachallenge.challenge.service.IChallengeService; +import com.itachallenge.challenge.service.IJwtService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import lombok.RequiredArgsConstructor; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Mono; + +@RestController +@Validated +@RequestMapping(value = "/itachallenge/api/v1/challenge") +@RequiredArgsConstructor +public class ChallengeSolvedController { + + private static final Logger log = LoggerFactory.getLogger(ChallengeSolvedController.class); + + private IChallengeService challengeService; + private IJwtService jwtService; + + @PostMapping("/challenges/{challengeId}/solved") + @Operation( + operationId = "Add a challenge to User's solved challenges.", + summary = "Add a challenge to solved challenges.", + description = "The ID Challenge sent through the URI is added to the user's solved challenges. User Id is determined from the headers.", + responses = { + @ApiResponse(responseCode = "200", content = {@Content(schema = @Schema(implementation = SolvedDto.class), mediaType = "application/json")}), + @ApiResponse(responseCode = "400", description = "Missing or invalid authorization header."), + @ApiResponse(responseCode = "404", description = "The Challenge with given Id was not found."), + @ApiResponse(responseCode = "500", description = "Internal Server Error") + } + ) + public Mono> addChallengeToSolved( + @PathVariable String challengeId, + @RequestHeader(name = "Authorization", required = false) String authHeader) { + return Mono.fromCallable(() -> jwtService.getUserUuIdFromAuthenticationHeader(authHeader)) + .onErrorMap(JwtException.class, e -> new BadRequestException(e.getMessage())) + .flatMap(userId -> challengeService.addChallengeToSolved(challengeId, userId)) + .doOnError(error -> log.error("Error adding challenge to solved: {}", error.getMessage())) + .map(ResponseEntity::ok); + } +} From c953171e98384e8dee2098f5602a0b7fcd99a944 Mon Sep 17 00:00:00 2001 From: RustyGearBox Date: Mon, 5 May 2025 14:08:37 +0200 Subject: [PATCH 52/63] added new test solvedController --- .../ChallengeSolvedControllerTest.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ChallengeSolvedControllerTest.java diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ChallengeSolvedControllerTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ChallengeSolvedControllerTest.java new file mode 100644 index 000000000..c570d4a96 --- /dev/null +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ChallengeSolvedControllerTest.java @@ -0,0 +1,97 @@ +package com.itachallenge.challenge.controller; + +import com.itachallenge.challenge.dto.SolvedDto; +import com.itachallenge.challenge.exception.BadRequestException; +import com.itachallenge.challenge.exception.JwtException; +import com.itachallenge.challenge.service.IChallengeService; +import com.itachallenge.challenge.service.IJwtService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.http.ResponseEntity; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; + + +class ChallengeSolvedControllerTest { + + @Mock + private IChallengeService challengeService; + + @Mock + private IJwtService jwtService; + + @InjectMocks + private ChallengeSolvedController challengeSolvedController; + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void addChallengeToSolved_Success() { + String challengeId = "123"; + String authHeader = "Bearer validToken"; + String userId = "user123"; + SolvedDto solvedDto = new SolvedDto(); + + when(jwtService.getUserUuIdFromAuthenticationHeader(authHeader)).thenReturn(userId); + when(challengeService.addChallengeToSolved(challengeId, userId)).thenReturn(Mono.just(solvedDto)); + + Mono> result = challengeSolvedController.addChallengeToSolved(challengeId, authHeader); + + StepVerifier.create(result) + .assertNext(response -> { + assertEquals(200, response.getStatusCodeValue()); + assertEquals(solvedDto, response.getBody()); + }) + .verifyComplete(); + + verify(jwtService, times(1)).getUserUuIdFromAuthenticationHeader(authHeader); + verify(challengeService, times(1)).addChallengeToSolved(challengeId, userId); + } + + @Test + void addChallengeToSolved_InvalidAuthHeader() { + String challengeId = "123"; + String authHeader = "invalidToken"; + + when(jwtService.getUserUuIdFromAuthenticationHeader(authHeader)).thenThrow(new JwtException("Invalid token")); + + Mono> result = challengeSolvedController.addChallengeToSolved(challengeId, authHeader); + + StepVerifier.create(result) + .expectErrorMatches(throwable -> throwable instanceof BadRequestException && + throwable.getMessage().equals("Invalid token")) + .verify(); + + verify(jwtService, times(1)).getUserUuIdFromAuthenticationHeader(authHeader); + verifyNoInteractions(challengeService); + } + + @Test + void addChallengeToSolved_ServiceError() { + String challengeId = "123"; + String authHeader = "Bearer validToken"; + String userId = "user123"; + + when(jwtService.getUserUuIdFromAuthenticationHeader(authHeader)).thenReturn(userId); + when(challengeService.addChallengeToSolved(challengeId, userId)).thenReturn(Mono.error(new RuntimeException("Service error"))); + + Mono> result = challengeSolvedController.addChallengeToSolved(challengeId, authHeader); + + StepVerifier.create(result) + .expectErrorMatches(throwable -> throwable instanceof RuntimeException && + throwable.getMessage().equals("Service error")) + .verify(); + + verify(jwtService, times(1)).getUserUuIdFromAuthenticationHeader(authHeader); + verify(challengeService, times(1)).addChallengeToSolved(challengeId, userId); + } +} \ No newline at end of file From 697a57c625edc4cf8c38754d05c5d439f15fd923 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Mon, 28 Apr 2025 11:33:42 +0200 Subject: [PATCH 53/63] [feat] Obtener recursos asociados a un challengeId (servicio + repositorio + test) --- .../repository/ResourceRepository.java | 1 + .../challenge/service/IResourceService.java | 3 + .../service/ResourceServiceImpl.java | 17 ++++ .../repository/ResourceRepositoryTest.java | 46 ++++++++++ .../service/ResourceServiceImplTest.java | 86 +++++++++++++++++++ 5 files changed, 153 insertions(+) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/repository/ResourceRepository.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/repository/ResourceRepository.java index e989cb36f..18dfee3f8 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/repository/ResourceRepository.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/repository/ResourceRepository.java @@ -25,4 +25,5 @@ public interface ResourceRepository extends ReactiveSortingRepository saveAll(Flux resourceDocumentFlux); Mono deleteAll(); + Flux findByChallengeIdsContaining(UUID challengeId); } diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IResourceService.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IResourceService.java index 831dba342..9f4169d72 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IResourceService.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/IResourceService.java @@ -1,12 +1,15 @@ package com.itachallenge.challenge.service; import com.itachallenge.challenge.dto.*; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.util.UUID; public interface IResourceService { Mono createResource(ResourceDto resourceDto); + Flux getResourcesByChallengeId(UUID challengeId); } diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java index e3b5fd5de..25df1d9d4 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java @@ -11,6 +11,7 @@ import org.slf4j.LoggerFactory; import org.springframework.cache.annotation.CacheEvict; import org.springframework.stereotype.Service; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.*; @@ -135,4 +136,20 @@ private Mono saveResource(ResourceDto resourceDto) { }) .doOnError(error -> log.error("Error occurred when creating resource {}", error.getMessage())); } + + + public Flux getResourcesByChallengeId(UUID challengeId) { + + if (challengeId == null) { + return Flux.error(new IllegalArgumentException("Challenge ID cannot be null")); + } + + + return resourceRepository.findByChallengeIdsContaining(challengeId) + .map(resourceDoc -> resourceConverter.convertDocumentToDto(resourceDoc, ResourceDto.class)) + .onErrorResume(error -> { + log.error("Error fetching resources: {}", error.getMessage()); + return Flux.error(new RuntimeException("Server error while fetching resources")); + }); + } } \ No newline at end of file diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java index 88a14a770..7d8a8e53f 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java @@ -251,4 +251,50 @@ void deleteAllResourcesTest() { .expectNext(0L) .verifyComplete(); } + + // Revisar + limpiar esto + // este test Verifica que el método findByChallengeIdsContaining + // del repositorio devuelve únicamente los recursos que contienen + // el challengeId especificado en su lista de retos asociados. + @Test + void findByChallengeIdsContaining_WhenChallengeIdExists_ReturnsResources() { + + UUID targetChallengeId = UUID.fromString("8ecbfe54-fec8-11ed-be56-0242ac120002"); + + + ResourceDocument resourceWithTargetChallenge = ResourceDocument.builder() + .resourceId(uuid1) + .title("Title1") + .description("Description1") + .url("http://example.com/resource1") + .topic(Topic.DEBUGGING) + .contentType(ResourceContentType.BLOG) + .challengeIds(List.of(targetChallengeId)) + .build(); + + ResourceDocument resourceWithoutTargetChallenge = ResourceDocument.builder() + .resourceId(uuid2) + .title("Title2") + .description("Description2") + .url("http://example.com/resource2") + .topic(Topic.DEBUGGING) + .contentType(ResourceContentType.BLOG) + .challengeIds(List.of(UUID.randomUUID())) + .build(); + + + resourceRepository.deleteAll().block(); + resourceRepository.saveAll(Flux.just(resourceWithTargetChallenge, resourceWithoutTargetChallenge)).blockLast(); + + + Flux result = resourceRepository.findByChallengeIdsContaining(targetChallengeId); + + + StepVerifier.create(result) + .expectNextMatches(resource -> + resource.getChallengeIds().contains(targetChallengeId) && + resource.getResourceId().equals(uuid1) + ) + .verifyComplete(); + } } diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java index 2a2bba86d..0d1a84bc7 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java @@ -14,6 +14,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import static org.mockito.ArgumentMatchers.*; @@ -416,5 +417,90 @@ void createResource_WithFailedConversion_ShouldThrowError() { .verify(); } + // ID CON recursos + @Test + void getResourcesByChallengeId_WhenResourcesExist_ReturnsFluxOfResources() { + + UUID challengeId = UUID.randomUUID(); + UUID resourceId1 = UUID.randomUUID(); + UUID resourceId2 = UUID.randomUUID(); + + + ResourceDocument doc1 = new ResourceDocument( + resourceId1, "Resource 1", "Desc 1", "https://example.com/1", + Topic.DEBUGGING, ResourceContentType.VIDEO, List.of(challengeId), AssociationType.ALLSAMETOPIC + ); + ResourceDocument doc2 = new ResourceDocument( + resourceId2, "Resource 2", "Desc 2", "https://example.com/2", + Topic.COMPONENTS, ResourceContentType.BLOG, List.of(challengeId), AssociationType.CHOOSE + ); + + ResourceDto dto1 = new ResourceDto(); + dto1.setResourceId(resourceId1); + dto1.setTitle("Resource 1"); + + + ResourceDto dto2 = new ResourceDto(); + dto2.setResourceId(resourceId2); + dto2.setTitle("Resource 2"); + + + when(resourceRepository.findByChallengeIdsContaining(challengeId)) + .thenReturn(Flux.just(doc1, doc2)); + when(resourceConverter.convertDocumentToDto(doc1, ResourceDto.class)).thenReturn(dto1); + when(resourceConverter.convertDocumentToDto(doc2, ResourceDto.class)).thenReturn(dto2); + + + StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) + .expectNext(dto1) + .expectNext(dto2) + .verifyComplete(); + } + + // Valido SIN recursos + @Test + void getResourcesByChallengeId_WhenNoResourcesExist_ReturnsEmptyFlux() { + + UUID challengeId = UUID.randomUUID(); + when(resourceRepository.findByChallengeIdsContaining(challengeId)) + .thenReturn(Flux.empty()); + + + StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) + .expectNextCount(0) + .verifyComplete(); + } + + //ID Nulo + @Test + void getResourcesByChallengeId_WhenIdIsNull_ThrowsIllegalArgumentException() { + + StepVerifier.create(resourceService.getResourcesByChallengeId(null)) + .expectErrorMatches(ex -> + ex instanceof IllegalArgumentException && + ex.getMessage().equals("Challenge ID cannot be null") + ) + .verify(); + } + + //Error en el repo + @Test + void getResourcesByChallengeId_WhenRepositoryFails_PropagatesError() { + + UUID challengeId = UUID.randomUUID(); + when(resourceRepository.findByChallengeIdsContaining(challengeId)) + .thenReturn(Flux.error(new RuntimeException("DB Connection Failed"))); + + + StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) + .expectErrorMatches(ex -> + ex instanceof RuntimeException && + ex.getMessage().equals("Server error while fetching resources") + ) + .verify(); + } + + + } From 86a302616a493425ee963d337478a2d94e4fbac8 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 10:09:47 +0200 Subject: [PATCH 54/63] Eliminados comentarios innecesarios --- .../challenge/repository/ResourceRepositoryTest.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java index 7d8a8e53f..b64a54095 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/repository/ResourceRepositoryTest.java @@ -252,10 +252,7 @@ void deleteAllResourcesTest() { .verifyComplete(); } - // Revisar + limpiar esto - // este test Verifica que el método findByChallengeIdsContaining - // del repositorio devuelve únicamente los recursos que contienen - // el challengeId especificado en su lista de retos asociados. + @Test void findByChallengeIdsContaining_WhenChallengeIdExists_ReturnsResources() { From 801810243ee78f73832c3d0c8e045d0e48263b80 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 13:06:02 +0200 Subject: [PATCH 55/63] =?UTF-8?q?feat:=20a=C3=B1adir=20controlador=20y=20t?= =?UTF-8?q?ests=20para=20los=20recursos=20del=20reto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ResourceController.java | 21 ++++++++ .../controller/ResourceControllerTest.java | 52 +++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java index 0ce54e738..d5450803f 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java @@ -6,13 +6,17 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import java.util.UUID; + @RestController @Validated @RequestMapping(value = "/itachallenge/api/v1/resource") @@ -41,5 +45,22 @@ public Mono> createNewResource(@RequestBody @Valid R return resourceService.createResource(resourceDto) .map(createdResource -> ResponseEntity.ok().body(createdResource)); } + + @GetMapping + @Operation( + summary = "Get resources by Challenge ID", + description = "Retrieve all resources associated with a specific challenge.", + responses = { + @ApiResponse(responseCode = "200", description = "Resources found", content = @Content(schema = @Schema(implementation = ResourceDto.class))), + @ApiResponse(responseCode = "400", description = "Invalid challenge ID"), + @ApiResponse(responseCode = "500", description = "Internal server error") + } + ) + public Flux getResourcesByChallengeId( + @RequestParam @NotNull UUID challengeId + ) { + log.info("Fetching resources for challenge ID: {}", challengeId); + return resourceService.getResourcesByChallengeId(challengeId); + } } diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java index eb50a4f21..72f4c3ca7 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java @@ -13,6 +13,7 @@ import org.springframework.http.MediaType; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.reactive.server.WebTestClient; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import static org.junit.Assert.*; @@ -108,4 +109,55 @@ void createNewResource_MissingRequiredFields_ReturnsBadRequest() { } + + @Test + void getResourcesByChallengeId_ValidId_ReturnsResources() { + // 1. Datos de prueba + UUID challengeId = UUID.randomUUID(); + ResourceDto resource1 = ResourceDto.builder() + .resourceId(UUID.randomUUID()) + .title("Resource 1") + .description("Desc 1") + .url("https://example.com/1") + .topic(Topic.DEBUGGING) + .contentType(ResourceContentType.VIDEO) + .challengeIds(List.of(challengeId)) + .build(); + + ResourceDto resource2 = ResourceDto.builder() + .resourceId(UUID.randomUUID()) + .title("Resource 2") + .description("Desc 2") + .url("https://example.com/2") + .topic(Topic.COMPONENTS) + .contentType(ResourceContentType.BLOG) + .challengeIds(List.of(challengeId)) + .build(); + + // 2. Mock del servicio + when(resourceService.getResourcesByChallengeId(challengeId)) + .thenReturn(Flux.just(resource1, resource2)); + + // 3. Ejecutar y verificar la petición HTTP + webTestClient.get() + .uri(uriBuilder -> uriBuilder + .path("/itachallenge/api/v1/resource") + .queryParam("challengeId", challengeId.toString()) + .build()) + .exchange() + .expectStatus().isOk() + .expectBodyList(ResourceDto.class) + .hasSize(2) + .value(resources -> { + assertEquals(resource1.getTitle(), resources.get(0).getTitle()); + assertEquals(resource2.getUrl(), resources.get(1).getUrl()); + }); + + // 4. Verificar interacción con el servicio + verify(resourceService, times(1)).getResourcesByChallengeId(challengeId); + } + + + + } From 369c6ea0eeb80c99d7b9a47fe2ac9984149b73d8 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 13:12:18 +0200 Subject: [PATCH 56/63] =?UTF-8?q?faltaba=20a=C3=B1adir=20la=20ruta=20del?= =?UTF-8?q?=20get?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itachallenge/challenge/controller/ResourceController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java index d5450803f..9e7c51e24 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java @@ -46,7 +46,7 @@ public Mono> createNewResource(@RequestBody @Valid R .map(createdResource -> ResponseEntity.ok().body(createdResource)); } - @GetMapping + @GetMapping("/challenge/resources") @Operation( summary = "Get resources by Challenge ID", description = "Retrieve all resources associated with a specific challenge.", From 9d12e92a5f93e0cf2cec1a3d06268e0d41ac7e99 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 13:14:33 +0200 Subject: [PATCH 57/63] =?UTF-8?q?correcci=C3=B3n=20de=20nomenclatura=20de?= =?UTF-8?q?=20ruta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../itachallenge/challenge/controller/ResourceController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java index 9e7c51e24..c5996c0a6 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java @@ -46,7 +46,7 @@ public Mono> createNewResource(@RequestBody @Valid R .map(createdResource -> ResponseEntity.ok().body(createdResource)); } - @GetMapping("/challenge/resources") + @GetMapping("/resources") @Operation( summary = "Get resources by Challenge ID", description = "Retrieve all resources associated with a specific challenge.", From c0a148355377745fe5ef1287d4f8eb1ef9e53570 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 13:37:17 +0200 Subject: [PATCH 58/63] Fix: updated ResourceControllerTest to match updated endpoint path --- .../challenge/controller/ResourceControllerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java index 72f4c3ca7..27fe6e469 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java @@ -141,7 +141,7 @@ void getResourcesByChallengeId_ValidId_ReturnsResources() { // 3. Ejecutar y verificar la petición HTTP webTestClient.get() .uri(uriBuilder -> uriBuilder - .path("/itachallenge/api/v1/resource") + .path("/itachallenge/api/v1/resource/resources") .queryParam("challengeId", challengeId.toString()) .build()) .exchange() From e98ceef9c9c95bfb84e4a8a7278325d048346bfc Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Tue, 29 Apr 2025 13:42:08 +0200 Subject: [PATCH 59/63] Added endpoint to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e11e13cd..7d512a8b0 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +* Issue #852: Added GET endpoint in the Challenge Microservice to retrieve resources associated to a challenge * Issue #849: Added GET endpoint to retrieve all User's bookmarked challenges. * Issue #843: Added DELETE endpoint in the Challenge Microservice to unbookmark a challenge. +* Issue #852: Added GET endpoint for retrieving resources from a challenge * Issue #829: Added bookmark POST endpoint in the Challenge Microservice to bookmark a challenge. * Issue #827: Added bookmark POST endpoint in the User Microservice to bookmark a challenge. * Issue #831: Added DELETE endpoint for user's bookmals in User Microservice. From bdf6cc42642641707700c256cba72516a4aaf25e Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Thu, 1 May 2025 11:27:44 +0200 Subject: [PATCH 60/63] fix: service method improved with custom exceptions --- .../challenge/service/ResourceServiceImpl.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java index 25df1d9d4..c9caf94d7 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java @@ -5,6 +5,9 @@ import com.itachallenge.challenge.dto.ResourceDto; import com.itachallenge.challenge.enums.AssociationType; import com.itachallenge.challenge.enums.Topic; +import com.itachallenge.challenge.exception.BadRequestException; +import com.itachallenge.challenge.exception.InternalServerErrorException; +import com.itachallenge.challenge.exception.ResourceNotFoundException; import com.itachallenge.challenge.helper.DocumentToDtoConverter; import com.itachallenge.challenge.repository.ResourceRepository; import org.slf4j.Logger; @@ -141,15 +144,18 @@ private Mono saveResource(ResourceDto resourceDto) { public Flux getResourcesByChallengeId(UUID challengeId) { if (challengeId == null) { - return Flux.error(new IllegalArgumentException("Challenge ID cannot be null")); + throw new BadRequestException("Challenge ID cannot be null"); } return resourceRepository.findByChallengeIdsContaining(challengeId) .map(resourceDoc -> resourceConverter.convertDocumentToDto(resourceDoc, ResourceDto.class)) .onErrorResume(error -> { - log.error("Error fetching resources: {}", error.getMessage()); - return Flux.error(new RuntimeException("Server error while fetching resources")); + log.error("Error fetching resources for challenge ID {}: {}", challengeId, error.getMessage()); + if (error instanceof ResourceNotFoundException) { + return Flux.error(error); + } + throw new InternalServerErrorException("Failed to fetch resources for challenge ID: " + challengeId); }); } } \ No newline at end of file From d90f4835d7e2dee1dfc0698ee0f8d552dad07cd3 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Thu, 1 May 2025 11:43:31 +0200 Subject: [PATCH 61/63] Added tests for exceptions and changed the method for a more reactive approach --- .../service/ResourceServiceImpl.java | 28 +++++++-------- .../service/ResourceServiceImplTest.java | 34 ++++++++++++++----- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java index c9caf94d7..69f4be8a8 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java @@ -142,20 +142,18 @@ private Mono saveResource(ResourceDto resourceDto) { public Flux getResourcesByChallengeId(UUID challengeId) { - - if (challengeId == null) { - throw new BadRequestException("Challenge ID cannot be null"); - } - - - return resourceRepository.findByChallengeIdsContaining(challengeId) - .map(resourceDoc -> resourceConverter.convertDocumentToDto(resourceDoc, ResourceDto.class)) - .onErrorResume(error -> { - log.error("Error fetching resources for challenge ID {}: {}", challengeId, error.getMessage()); - if (error instanceof ResourceNotFoundException) { - return Flux.error(error); - } - throw new InternalServerErrorException("Failed to fetch resources for challenge ID: " + challengeId); - }); + return Mono.justOrEmpty(challengeId) + .switchIfEmpty(Mono.error(new BadRequestException("Challenge ID cannot be null"))) + .flatMapMany(validChallengeId -> + resourceRepository.findByChallengeIdsContaining(validChallengeId) + .map(resourceDoc -> resourceConverter.convertDocumentToDto(resourceDoc, ResourceDto.class)) + .onErrorResume(error -> { + log.error("Error fetching resources for challenge ID {}: {}", validChallengeId, error.getMessage()); + if (error instanceof ResourceNotFoundException) { + return Flux.error(error); + } + return Flux.error(new InternalServerErrorException("Failed to fetch resources for challenge ID: " + validChallengeId)); + }) + ); } } \ No newline at end of file diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java index 0d1a84bc7..79d21ce00 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java @@ -7,6 +7,9 @@ import com.itachallenge.challenge.enums.AssociationType; import com.itachallenge.challenge.enums.ResourceContentType; import com.itachallenge.challenge.enums.Topic; +import com.itachallenge.challenge.exception.BadRequestException; +import com.itachallenge.challenge.exception.InternalServerErrorException; +import com.itachallenge.challenge.exception.ResourceNotFoundException; import com.itachallenge.challenge.helper.DocumentToDtoConverter; import com.itachallenge.challenge.repository.ResourceRepository; import org.junit.jupiter.api.Test; @@ -471,13 +474,11 @@ void getResourcesByChallengeId_WhenNoResourcesExist_ReturnsEmptyFlux() { .verifyComplete(); } - //ID Nulo @Test - void getResourcesByChallengeId_WhenIdIsNull_ThrowsIllegalArgumentException() { - + void getResourcesByChallengeId_WhenIdIsNull_ThrowsBadRequestException() { StepVerifier.create(resourceService.getResourcesByChallengeId(null)) .expectErrorMatches(ex -> - ex instanceof IllegalArgumentException && + ex instanceof BadRequestException && ex.getMessage().equals("Challenge ID cannot be null") ) .verify(); @@ -485,17 +486,32 @@ void getResourcesByChallengeId_WhenIdIsNull_ThrowsIllegalArgumentException() { //Error en el repo @Test - void getResourcesByChallengeId_WhenRepositoryFails_PropagatesError() { - + void getResourcesByChallengeId_WhenRepositoryFails_ThrowsInternalServerErrorException() { UUID challengeId = UUID.randomUUID(); when(resourceRepository.findByChallengeIdsContaining(challengeId)) .thenReturn(Flux.error(new RuntimeException("DB Connection Failed"))); - StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) .expectErrorMatches(ex -> - ex instanceof RuntimeException && - ex.getMessage().equals("Server error while fetching resources") + ex instanceof InternalServerErrorException && + ex.getMessage().equals("Failed to fetch resources for challenge ID: " + challengeId) + ) + .verify(); + + verify(resourceRepository, times(1)).findByChallengeIdsContaining(challengeId); + } + + @Test + void getResourcesByChallengeId_WhenResourceNotFound_PropagatesException() { + UUID challengeId = UUID.randomUUID(); + ResourceNotFoundException ex = new ResourceNotFoundException("Not found"); + when(resourceRepository.findByChallengeIdsContaining(challengeId)) + .thenReturn(Flux.error(ex)); + + StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) + .expectErrorMatches(thrownEx -> + thrownEx instanceof ResourceNotFoundException && + thrownEx.getMessage().equals("Not found") ) .verify(); } From 962874a2d48009fd010f83d092f2074980c7ad15 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Sun, 4 May 2025 18:12:53 +0200 Subject: [PATCH 62/63] Refactorizado el manejo de ResourceNotFoundException en el Global Exception Handler y corregido la respuesta 404 en los controladores. --- .../controller/ResourceController.java | 31 +++---- .../exception/GlobalExceptionHandler.java | 2 +- .../service/ResourceServiceImpl.java | 1 + .../controller/ResourceControllerTest.java | 87 ++++++++++++------- .../service/ResourceServiceImplTest.java | 12 ++- 5 files changed, 79 insertions(+), 54 deletions(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java index c5996c0a6..4cce1fa78 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java @@ -1,17 +1,23 @@ package com.itachallenge.challenge.controller; import com.itachallenge.challenge.dto.ResourceDto; + import com.itachallenge.challenge.service.IResourceService; import io.swagger.v3.oas.annotations.Operation; + +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; -import jakarta.validation.constraints.NotNull; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; + import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; + import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -46,20 +52,15 @@ public Mono> createNewResource(@RequestBody @Valid R .map(createdResource -> ResponseEntity.ok().body(createdResource)); } - @GetMapping("/resources") - @Operation( - summary = "Get resources by Challenge ID", - description = "Retrieve all resources associated with a specific challenge.", - responses = { - @ApiResponse(responseCode = "200", description = "Resources found", content = @Content(schema = @Schema(implementation = ResourceDto.class))), - @ApiResponse(responseCode = "400", description = "Invalid challenge ID"), - @ApiResponse(responseCode = "500", description = "Internal server error") - } - ) - public Flux getResourcesByChallengeId( - @RequestParam @NotNull UUID challengeId - ) { - log.info("Fetching resources for challenge ID: {}", challengeId); + + @GetMapping("/challenge/{challengeId}") + @Operation(summary = "Get resources by challenge ID") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Resources found"), + @ApiResponse(responseCode = "400", description = "Invalid challenge ID"), + @ApiResponse(responseCode = "404", description = "No resources found") + }) + public Flux getResourcesByChallengeId(@PathVariable UUID challengeId) { return resourceService.getResourcesByChallengeId(challengeId); } } diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/exception/GlobalExceptionHandler.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/exception/GlobalExceptionHandler.java index f255484f6..3d55c4c65 100755 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/exception/GlobalExceptionHandler.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/exception/GlobalExceptionHandler.java @@ -62,7 +62,7 @@ public ResponseEntity handleChallengeNotFoundReturn404Exception(Chal @ExceptionHandler(ResourceNotFoundException.class) public ResponseEntity handleResourceNotFoundException(ResourceNotFoundException ex) { - return ResponseEntity.ok().body(new MessageDto(ex.getMessage())); + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new MessageDto(ex.getMessage())); } @ExceptionHandler(LanguageNotFoundException.class) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java index 69f4be8a8..55b4bd81b 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/service/ResourceServiceImpl.java @@ -146,6 +146,7 @@ public Flux getResourcesByChallengeId(UUID challengeId) { .switchIfEmpty(Mono.error(new BadRequestException("Challenge ID cannot be null"))) .flatMapMany(validChallengeId -> resourceRepository.findByChallengeIdsContaining(validChallengeId) + .switchIfEmpty(Mono.error(new ResourceNotFoundException("No resources found for challenge ID: " + validChallengeId))) // Lanzar 404 si no se encuentra nada .map(resourceDoc -> resourceConverter.convertDocumentToDto(resourceDoc, ResourceDto.class)) .onErrorResume(error -> { log.error("Error fetching resources for challenge ID {}: {}", validChallengeId, error.getMessage()); diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java index 27fe6e469..dc520e216 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/controller/ResourceControllerTest.java @@ -1,9 +1,11 @@ package com.itachallenge.challenge.controller; + import com.itachallenge.challenge.dto.ResourceDto; import com.itachallenge.challenge.enums.AssociationType; import com.itachallenge.challenge.enums.ResourceContentType; import com.itachallenge.challenge.enums.Topic; +import com.itachallenge.challenge.exception.ResourceNotFoundException; import com.itachallenge.challenge.service.IResourceService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -24,6 +26,7 @@ import static org.mockito.ArgumentMatchers.any; +import java.lang.reflect.Constructor; import java.util.List; import java.util.UUID; @@ -108,56 +111,78 @@ void createNewResource_MissingRequiredFields_ReturnsBadRequest() { verify(resourceService, never()).createResource(any(ResourceDto.class)); } - - @Test void getResourcesByChallengeId_ValidId_ReturnsResources() { - // 1. Datos de prueba - UUID challengeId = UUID.randomUUID(); - ResourceDto resource1 = ResourceDto.builder() - .resourceId(UUID.randomUUID()) - .title("Resource 1") - .description("Desc 1") - .url("https://example.com/1") - .topic(Topic.DEBUGGING) - .contentType(ResourceContentType.VIDEO) - .challengeIds(List.of(challengeId)) - .build(); - ResourceDto resource2 = ResourceDto.builder() + UUID challengeId = UUID.randomUUID(); + ResourceDto mockResource = ResourceDto.builder() .resourceId(UUID.randomUUID()) - .title("Resource 2") - .description("Desc 2") - .url("https://example.com/2") + .title("Test Resource") + .description("Test Description") + .url("http://test.com") .topic(Topic.COMPONENTS) - .contentType(ResourceContentType.BLOG) + .contentType(ResourceContentType.VIDEO) .challengeIds(List.of(challengeId)) + .associationType(AssociationType.ALLSAMETOPIC) .build(); - // 2. Mock del servicio when(resourceService.getResourcesByChallengeId(challengeId)) - .thenReturn(Flux.just(resource1, resource2)); + .thenReturn(Flux.just(mockResource)); + - // 3. Ejecutar y verificar la petición HTTP webTestClient.get() - .uri(uriBuilder -> uriBuilder - .path("/itachallenge/api/v1/resource/resources") - .queryParam("challengeId", challengeId.toString()) - .build()) + .uri("/itachallenge/api/v1/resource/challenge/" + challengeId) // Ruta completa .exchange() .expectStatus().isOk() .expectBodyList(ResourceDto.class) - .hasSize(2) - .value(resources -> { - assertEquals(resource1.getTitle(), resources.get(0).getTitle()); - assertEquals(resource2.getUrl(), resources.get(1).getUrl()); - }); + .hasSize(1) + .contains(mockResource); - // 4. Verificar interacción con el servicio verify(resourceService, times(1)).getResourcesByChallengeId(challengeId); } + @Test + void getResourcesByChallengeId_InvalidId_ReturnsBadRequest() { + + webTestClient.get() + .uri("/itachallenge/api/v1/resource/challenge/null") // Ruta completa con ID inválido + .exchange() + .expectStatus().isBadRequest(); + verify(resourceService, never()).getResourcesByChallengeId(any()); + } + + @Test + void getResourcesByChallengeId_NoResources_ReturnsNotFoundWithMessage() { + + UUID challengeId = UUID.randomUUID(); + String errorMessage = "No resources found for challenge ID: " + challengeId; + + when(resourceService.getResourcesByChallengeId(challengeId)) + .thenReturn(Flux.error(new ResourceNotFoundException(errorMessage))); + + + webTestClient.get() + .uri("/itachallenge/api/v1/resource/challenge/" + challengeId) + .exchange() + .expectStatus().isNotFound() + .expectBody() + .jsonPath("$.message").isEqualTo(errorMessage); + + verify(resourceService, times(1)).getResourcesByChallengeId(challengeId); + } + } + + + + + + + + + + + diff --git a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java index 79d21ce00..dd366dd3a 100644 --- a/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java +++ b/itachallenge-challenge/src/test/java/com/itachallenge/challenge/service/ResourceServiceImplTest.java @@ -460,18 +460,16 @@ void getResourcesByChallengeId_WhenResourcesExist_ReturnsFluxOfResources() { .verifyComplete(); } - // Valido SIN recursos @Test - void getResourcesByChallengeId_WhenNoResourcesExist_ReturnsEmptyFlux() { + void getResourcesByChallengeId_WhenNoResourcesExist_ThrowsResourceNotFoundException() { UUID challengeId = UUID.randomUUID(); when(resourceRepository.findByChallengeIdsContaining(challengeId)) - .thenReturn(Flux.empty()); - + .thenReturn(Flux.empty()); // Simula que no hay recursos StepVerifier.create(resourceService.getResourcesByChallengeId(challengeId)) - .expectNextCount(0) - .verifyComplete(); + .expectError(ResourceNotFoundException.class) + .verify(); } @Test @@ -484,7 +482,7 @@ void getResourcesByChallengeId_WhenIdIsNull_ThrowsBadRequestException() { .verify(); } - //Error en el repo + @Test void getResourcesByChallengeId_WhenRepositoryFails_ThrowsInternalServerErrorException() { UUID challengeId = UUID.randomUUID(); From 9db8e2ede1f7e877cc14905ce3b3ef99fc3ec444 Mon Sep 17 00:00:00 2001 From: Sh1ng0 Date: Mon, 5 May 2025 18:55:00 +0200 Subject: [PATCH 63/63] Added 500 error response to Swagger documentation in ResourceController --- .../itachallenge/challenge/controller/ResourceController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java index 4cce1fa78..0d61860de 100644 --- a/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java +++ b/itachallenge-challenge/src/main/java/com/itachallenge/challenge/controller/ResourceController.java @@ -58,7 +58,8 @@ public Mono> createNewResource(@RequestBody @Valid R @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Resources found"), @ApiResponse(responseCode = "400", description = "Invalid challenge ID"), - @ApiResponse(responseCode = "404", description = "No resources found") + @ApiResponse(responseCode = "404", description = "No resources found"), + @ApiResponse(responseCode = "500", description = "Internal Server Error") }) public Flux getResourcesByChallengeId(@PathVariable UUID challengeId) { return resourceService.getResourcesByChallengeId(challengeId);