diff --git a/skill-tree/src/test/java/com/RDS/skilltree/integration/EndorsementsIntegrationTests.java b/skill-tree/src/test/java/com/RDS/skilltree/integration/EndorsementsIntegrationTests.java index 6dd98f11..2792057b 100644 --- a/skill-tree/src/test/java/com/RDS/skilltree/integration/EndorsementsIntegrationTests.java +++ b/skill-tree/src/test/java/com/RDS/skilltree/integration/EndorsementsIntegrationTests.java @@ -67,6 +67,18 @@ private void cleanUp() { userRepository.deleteAll(); } + private UUID createEndorsementModel(Boolean isStatusPending) { + EndorsementStatus endorsementStatus; + if (isStatusPending) { + endorsementStatus = EndorsementStatus.PENDING; + } else { + endorsementStatus = EndorsementStatus.APPROVED; + } + EndorsementModel endorsementModel = + EndorsementModel.builder().status(endorsementStatus).build(); + return endorsementRepository.save(endorsementModel).getId(); + } + @Test @Disabled @DisplayName("Fetch all the endorsements") @@ -479,4 +491,99 @@ public void itShouldReturn401OnEndorsementSearchWithoutCookie() { equalTo( "The access token provided is expired, revoked, malformed, or invalid for other reasons.")); } + + @Test + @DisplayName( + "Return 200, when request is made using super user cookie and status is APPROVED/REJECTED") + public void + itShouldReturn200OnUpdateEndorsementStatusWithSuperUserCookieAndAcceptOrRejectEndorsementStatus() { + UUID endorsementId = createEndorsementModel(true); + Response response = + given() + .cookies(RestAPIHelper.getSuperUserCookie()) + .queryParam("status", EndorsementStatus.APPROVED.name()) + .patch("/v1/endorsements/{id}", endorsementId); + + response + .then() + .statusCode(200) + .body("data", equalTo(null)) + .body("message", equalTo("Successfully updated endorsement status")); + } + + @Test + @DisplayName( + "Return 403, when request is made without using super user cookie and status is APPROVED/REJECTED") + public void + itShouldReturn403OnUpdateEndorsementStatusWithOutSuperUserCookieAndAcceptOrRejectEndorsementStatus() { + UUID endorsementId = createEndorsementModel(true); + Response response = + given() + .cookies(RestAPIHelper.getUserCookie()) + .queryParam("status", EndorsementStatus.APPROVED.name()) + .patch("/v1/endorsements/{id}", endorsementId); + + response + .then() + .statusCode(403) + .body("data", equalTo(null)) + .body("message", equalTo("Unauthorized, Access is only available to super users")); + } + + @Test + @DisplayName( + "Return 400, when request is made with using super user cookie and status is invalid") + public void + itShouldReturn400OnUpdateEndorsementStatusWithSuperUserCookieAndEndorsementStatusIsInvalid() { + UUID endorsementId = createEndorsementModel(true); + Response response = + given() + .cookies(RestAPIHelper.getSuperUserCookie()) + .queryParam("status", "invalid-status") + .patch("/v1/endorsements/{id}", endorsementId); + + response + .then() + .statusCode(400) + .body("data", equalTo(null)) + .body("message", equalTo("Invalid parameter endorsement status: invalid-status")); + } + + @Test + @DisplayName( + "Return 400, when request is made with using super user cookie and status is PENDING") + public void + itShouldReturn400OnUpdateEndorsementStatusWithSuperUserCookieAndEndorsementStatusIsPending() { + UUID endorsementId = createEndorsementModel(true); + Response response = + given() + .cookies(RestAPIHelper.getSuperUserCookie()) + .queryParam("status", EndorsementStatus.PENDING.name()) + .patch("/v1/endorsements/{id}", endorsementId); + + response + .then() + .statusCode(400) + .body("data", equalTo(null)) + .body("message", equalTo("Invalid parameter endorsement status: PENDING")); + } + + @Test + @DisplayName( + "Return 409, when request is made with using super user cookie and endorsement is already updated") + public void + itShouldReturn409OnUpdateEndorsementStatusWithSuperUserCookieAndEndorsementAlreadyUpdated() { + UUID endorsementId = createEndorsementModel(false); + Response response = + given() + .cookies(RestAPIHelper.getSuperUserCookie()) + .queryParam("status", EndorsementStatus.APPROVED.name()) + .patch("/v1/endorsements/{id}", endorsementId); + + response + .then() + .statusCode(409) + .body("data", equalTo(null)) + .body("message", equalTo("Endorsement is already updated. Cannot modify status")); + } } diff --git a/skill-tree/src/test/java/com/RDS/skilltree/unit/EndorsementServiceTest.java b/skill-tree/src/test/java/com/RDS/skilltree/unit/EndorsementServiceTest.java index ef420899..2cf1caf7 100644 --- a/skill-tree/src/test/java/com/RDS/skilltree/unit/EndorsementServiceTest.java +++ b/skill-tree/src/test/java/com/RDS/skilltree/unit/EndorsementServiceTest.java @@ -3,17 +3,16 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; -import com.RDS.skilltree.Endorsement.EndorsementDRO; -import com.RDS.skilltree.Endorsement.EndorsementDTO; -import com.RDS.skilltree.Endorsement.EndorsementModel; -import com.RDS.skilltree.Endorsement.EndorsementModelFromJSON; -import com.RDS.skilltree.Endorsement.EndorsementRepository; -import com.RDS.skilltree.Endorsement.EndorsementServiceImpl; +import com.RDS.skilltree.Common.Response.GenericResponse; +import com.RDS.skilltree.Endorsement.*; +import com.RDS.skilltree.Exceptions.EntityAlreadyExistsException; +import com.RDS.skilltree.Exceptions.InvalidParameterException; import com.RDS.skilltree.Exceptions.NoEntityException; import com.RDS.skilltree.Skill.SkillModel; import com.RDS.skilltree.Skill.SkillRepository; import com.RDS.skilltree.User.UserModel; import com.RDS.skilltree.User.UserRepository; +import com.RDS.skilltree.User.UserRole; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.EntityNotFoundException; @@ -22,6 +21,7 @@ import java.time.Instant; import java.time.LocalDateTime; import java.util.*; +import org.junit.jupiter.api.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -34,6 +34,9 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.util.ReflectionTestUtils; @ExtendWith(MockitoExtension.class) @@ -48,12 +51,30 @@ public class EndorsementServiceTest { @InjectMocks @Autowired private EndorsementServiceImpl endorsementService; + @Mock private Authentication auth; + @BeforeEach public void setUp() { ReflectionTestUtils.setField( endorsementService, "dummyEndorsementDataPath", "dummy-data/endorsements.json"); } + @AfterEach + public void clearSecurityContext() { + SecurityContextHolder.clearContext(); + } + + private void setupUpdateEndorsementTests(Boolean useSuperUserRole) { + UserModel userModel = new UserModel(); + if (useSuperUserRole) { + userModel.setRole(UserRole.SUPERUSER); + } else { + userModel.setRole(UserRole.USER); + } + when(auth.getPrincipal()).thenReturn(userModel); + SecurityContextHolder.getContext().setAuthentication(auth); + } + @Test public void itShouldGetEndorsementsById() { UUID endorsementId = UUID.randomUUID(); @@ -588,4 +609,160 @@ void testCreateEndorsementWithInvalidSkill() { // Verify that save method is not called verify(endorsementRepository, never()).save(any(EndorsementModel.class)); } + + @Test + @DisplayName( + "Return unauthorized access, given user is not a super user to update endorsement status") + public void itShouldReturnUnauthorizedGivenUserIsNotSuperUser() { + setupUpdateEndorsementTests(false); + + UUID endorsementId = UUID.randomUUID(); + String status = EndorsementStatus.APPROVED.name(); + + AccessDeniedException exception = + assertThrows( + AccessDeniedException.class, + () -> endorsementService.updateEndorsementStatus(endorsementId, status)); + assertEquals("Unauthorized, Access is only available to super users", exception.getMessage()); + verify(endorsementRepository, never()).save(any(EndorsementModel.class)); + } + + @Test + @DisplayName("Return invalid status given status is pending") + public void itShouldReturnInvalidStatusGivenEndorsementStatusIsPending() { + setupUpdateEndorsementTests(true); + + UUID endorsementId = UUID.randomUUID(); + String status = EndorsementStatus.PENDING.name(); + + InvalidParameterException exception = + assertThrows( + InvalidParameterException.class, + () -> endorsementService.updateEndorsementStatus(endorsementId, status)); + assertEquals("Invalid parameter endorsement status: " + status, exception.getMessage()); + verify(endorsementRepository, never()).save(any(EndorsementModel.class)); + } + + @Test + @DisplayName("Return invalid status given status is invalid") + public void itShouldReturnInvalidStatusGivenInvalidEndorsementStatus() { + setupUpdateEndorsementTests(true); + + UUID endorsementId = UUID.randomUUID(); + String status = "invalid-status"; + + InvalidParameterException exception = + assertThrows( + InvalidParameterException.class, + () -> endorsementService.updateEndorsementStatus(endorsementId, status)); + assertEquals("Invalid parameter endorsement status: " + status, exception.getMessage()); + verify(endorsementRepository, never()).save(any(EndorsementModel.class)); + } + + @Test + @DisplayName("Return cannot modify status given status is already updated") + public void itShouldThrowEntityAlreadyExistsExceptionGivenEndorsementIsUpdated() { + setupUpdateEndorsementTests(true); + + UUID userId = UUID.randomUUID(); + UUID skillId = UUID.randomUUID(); + UUID endorsementId = UUID.randomUUID(); + + UserModel mockUser = UserModel.builder().id(userId).build(); + SkillModel mockSkill = SkillModel.builder().id(skillId).build(); + EndorsementModel mockEndorsement = + EndorsementModel.builder() + .id(endorsementId) + .status(EndorsementStatus.APPROVED) + .user(mockUser) + .skill(mockSkill) + .build(); + mockEndorsement.setCreatedAt(Instant.now()); + mockEndorsement.setUpdatedAt(Instant.now()); + mockEndorsement.setCreatedBy(mockUser); + mockEndorsement.setUpdatedBy(mockUser); + + when(endorsementRepository.findById(endorsementId)).thenReturn(Optional.of(mockEndorsement)); + + EntityAlreadyExistsException exception = + assertThrows( + EntityAlreadyExistsException.class, + () -> + endorsementService.updateEndorsementStatus( + endorsementId, EndorsementStatus.APPROVED.name())); + assertEquals("Endorsement is already updated. Cannot modify status", exception.getMessage()); + verify(endorsementRepository, never()).save(any(EndorsementModel.class)); + } + + @Test + @DisplayName("Return endorsement not found given an unknown endorsement id") + public void itShouldReturnEndorsementNotFoundGivenUnknownEndorsementId() { + setupUpdateEndorsementTests(true); + + UUID nonExistentEndorsementId = UUID.randomUUID(); + String status = EndorsementStatus.APPROVED.name(); + + when(endorsementRepository.findById(nonExistentEndorsementId)).thenReturn(Optional.empty()); + + NoEntityException exception = + assertThrows( + NoEntityException.class, + () -> endorsementService.updateEndorsementStatus(nonExistentEndorsementId, status)); + assertEquals( + "No endorsement with id " + nonExistentEndorsementId + " was found", + exception.getMessage()); + verify(endorsementRepository, never()).save(any(EndorsementModel.class)); + } + + @Test + @DisplayName( + "Update endorsement status given a valid endorsement id and status is approved or rejected") + public void itShouldUpdateEndorsementStatusGivenEndorsementIdAndStatusApprovedOrRejected() { + setupUpdateEndorsementTests(true); + + UUID userId = UUID.randomUUID(); + UUID skillId = UUID.randomUUID(); + UUID endorsementId = UUID.randomUUID(); + EndorsementStatus status = EndorsementStatus.APPROVED; + + UserModel mockUser = UserModel.builder().id(userId).build(); + SkillModel mockSkill = SkillModel.builder().id(skillId).build(); + EndorsementModel mockEndorsement = + EndorsementModel.builder() + .id(endorsementId) + .status(EndorsementStatus.PENDING) + .user(mockUser) + .skill(mockSkill) + .build(); + mockEndorsement.setCreatedAt(Instant.now()); + mockEndorsement.setUpdatedAt(Instant.now()); + mockEndorsement.setCreatedBy(mockUser); + mockEndorsement.setUpdatedBy(mockUser); + + when(endorsementRepository.findById(endorsementId)).thenReturn(Optional.of(mockEndorsement)); + + GenericResponse result = + endorsementService.updateEndorsementStatus(endorsementId, status.name()); + assertEquals("Successfully updated endorsement status", result.getMessage()); + + verify(endorsementRepository, times(1)).save(any(EndorsementModel.class)); + + EndorsementModel updatedMockEndorsement = + EndorsementModel.builder() + .id(endorsementId) + .user(mockUser) + .skill(mockSkill) + .status(EndorsementStatus.APPROVED) + .build(); + mockEndorsement.setCreatedAt(Instant.now()); + mockEndorsement.setUpdatedAt(Instant.now()); + mockEndorsement.setCreatedBy(mockUser); + mockEndorsement.setUpdatedBy(mockUser); + + when(endorsementRepository.findById(endorsementId)) + .thenReturn(Optional.of(updatedMockEndorsement)); + Optional updatedEndorsement = endorsementRepository.findById(endorsementId); + assertTrue(updatedEndorsement.isPresent()); + assertEquals(EndorsementStatus.APPROVED, updatedEndorsement.get().getStatus()); + } }