diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/UserControllerFT.java b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/UserControllerFT.java index 19b193a970..1ca758e58e 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/UserControllerFT.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/UserControllerFT.java @@ -1,11 +1,18 @@ package uk.gov.hmcts.reform.preapi.controllers; import com.fasterxml.jackson.core.JsonProcessingException; +import io.restassured.response.Response; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; import uk.gov.hmcts.reform.preapi.controllers.params.TestingSupportRoles; import uk.gov.hmcts.reform.preapi.dto.CreateAppAccessDTO; +import uk.gov.hmcts.reform.preapi.dto.CreatePortalAccessDTO; import uk.gov.hmcts.reform.preapi.dto.CreateUserDTO; +import uk.gov.hmcts.reform.preapi.dto.PortalAccessDTO; +import uk.gov.hmcts.reform.preapi.dto.UserDTO; +import uk.gov.hmcts.reform.preapi.enums.AccessStatus; import uk.gov.hmcts.reform.preapi.util.FunctionalTestBase; import java.util.Set; @@ -65,31 +72,52 @@ void shouldCreateUserAsLevel1() throws JsonProcessingException { assertPutResponseMatchesDto(dto); } - @DisplayName("Scenario: Put user as 'Level 2' role") - @Test - void shouldCreateUserAsLevel2() throws JsonProcessingException { - var dto = createUserDto(); + @ParameterizedTest + @DisplayName("Scenario: Put user as non-admin") + @EnumSource(value = TestingSupportRoles.class, names = {"SUPER_USER", "LEVEL_1"}, mode = EnumSource.Mode.EXCLUDE) + void shouldFailToCreateUserAsNonAdmin(TestingSupportRoles role) throws JsonProcessingException { + CreateUserDTO dto = createUserDto(); + Response createResponse = putUser(dto, role); - var createResponse = putUser(dto, TestingSupportRoles.LEVEL_2); assertResponseCode(createResponse, 403); } - @DisplayName("Scenario: Put user as 'Level 3' role") @Test - void shouldCreateUserAsLevel3() throws JsonProcessingException { - var dto = createUserDto(); - - var createResponse = putUser(dto, TestingSupportRoles.LEVEL_3); - assertResponseCode(createResponse, 403); - } - - @DisplayName("Scenario: Put user as 'Level 4' role") - @Test - void shouldCreateUserAsLevel4() throws JsonProcessingException { - var dto = createUserDto(); - - var createResponse = putUser(dto, TestingSupportRoles.LEVEL_4); - assertResponseCode(createResponse, 403); + @DisplayName("Portal access registered at value should be set when active") + void shouldAutomaticallySetRegisteredAtWhenNotSetOnActive() throws JsonProcessingException { + // create a user + CreateUserDTO dto = createUserDto(); + Response putUser1 = putUser(dto, TestingSupportRoles.SUPER_USER); + assertResponseCode(putUser1, 201); + + // add new portal access in the invited status + Response createPortalAccessResponse = doPostRequest( + "/testing-support/create-portal-access-without-email-invite/" + dto.getId(), + TestingSupportRoles.SUPER_USER); + UUID portalAccessId = createPortalAccessResponse.jsonPath().getUUID("portalAccessId"); + + // check portal access + UserDTO userDto1 = getUserById(dto.getId()); + assertThat(userDto1.getPortalAccess()).isNotEmpty(); + PortalAccessDTO portalAccessDto1 = userDto1.getPortalAccess().getFirst(); + assertThat(portalAccessDto1.getId()).isEqualTo(portalAccessId); + assertThat(portalAccessDto1.getRegisteredAt()).isNull(); + assertThat(portalAccessDto1.getStatus()).isEqualTo(AccessStatus.INVITATION_SENT); + + // update portal access to active without setting registered at + CreatePortalAccessDTO updatedPortalAccess = new CreatePortalAccessDTO(portalAccessDto1); + updatedPortalAccess.setStatus(AccessStatus.ACTIVE); + dto.setPortalAccess(Set.of(updatedPortalAccess)); + Response putUser2 = putUser(dto, TestingSupportRoles.SUPER_USER); + assertResponseCode(putUser2, 204); + + // check registered at set + UserDTO userDto2 = getUserById(dto.getId()); + assertThat(userDto2.getPortalAccess()).isNotEmpty(); + PortalAccessDTO portalAccessDto2 = userDto2.getPortalAccess().getFirst(); + assertThat(portalAccessDto2.getId()).isEqualTo(portalAccessId); + assertThat(portalAccessDto2.getRegisteredAt()).isNotNull(); + assertThat(portalAccessDto2.getStatus()).isEqualTo(AccessStatus.ACTIVE); } @DisplayName("Scenario: Duplicate email address should fail") @@ -241,6 +269,12 @@ void userFilteredByAppActiveStatus() throws JsonProcessingException { .isEqualTo(0); } + private UserDTO getUserById(UUID userId) { + Response response = doGetRequest(USERS_ENDPOINT + "/" + userId, TestingSupportRoles.SUPER_USER); + assertResponseCode(response, 200); + return response.getBody().jsonPath().getObject("", UserDTO.class); + } + private void assertPutResponseMatchesDto(CreateUserDTO dto) { var getResponse = doGetRequest(USERS_ENDPOINT + "/" + dto.getId(), TestingSupportRoles.SUPER_USER); assertResponseCode(getResponse, 200); diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/TestingSupportController.java b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/TestingSupportController.java index 0cb2b5a8a2..5a16936c49 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/TestingSupportController.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/TestingSupportController.java @@ -33,11 +33,13 @@ import uk.gov.hmcts.reform.preapi.entities.Case; import uk.gov.hmcts.reform.preapi.entities.Court; import uk.gov.hmcts.reform.preapi.entities.Participant; +import uk.gov.hmcts.reform.preapi.entities.PortalAccess; import uk.gov.hmcts.reform.preapi.entities.Recording; import uk.gov.hmcts.reform.preapi.entities.Region; import uk.gov.hmcts.reform.preapi.entities.Role; import uk.gov.hmcts.reform.preapi.entities.TermsAndConditions; import uk.gov.hmcts.reform.preapi.entities.User; +import uk.gov.hmcts.reform.preapi.enums.AccessStatus; import uk.gov.hmcts.reform.preapi.enums.CourtType; import uk.gov.hmcts.reform.preapi.enums.ParticipantType; import uk.gov.hmcts.reform.preapi.enums.RecordingOrigin; @@ -53,6 +55,7 @@ import uk.gov.hmcts.reform.preapi.repositories.CaseRepository; import uk.gov.hmcts.reform.preapi.repositories.CourtRepository; import uk.gov.hmcts.reform.preapi.repositories.ParticipantRepository; +import uk.gov.hmcts.reform.preapi.repositories.PortalAccessRepository; import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; import uk.gov.hmcts.reform.preapi.repositories.RegionRepository; import uk.gov.hmcts.reform.preapi.repositories.RoleRepository; @@ -93,6 +96,7 @@ class TestingSupportController { private final ScheduledTaskRunner scheduledTaskRunner; private final AzureFinalStorageService azureFinalStorageService; private final MigrationRecordRepository migrationRecordRepository; + private final PortalAccessRepository portalAccessRepository; @Autowired TestingSupportController(final BookingRepository bookingRepository, @@ -110,7 +114,8 @@ class TestingSupportController { final ScheduledTaskRunner scheduledTaskRunner, final AuditRepository auditRepository, final AzureFinalStorageService azureFinalStorageService, - final MigrationRecordRepository migrationRecordRepository) { + final MigrationRecordRepository migrationRecordRepository, + final PortalAccessRepository portalAccessRepository) { this.bookingRepository = bookingRepository; this.captureSessionRepository = captureSessionRepository; this.caseRepository = caseRepository; @@ -127,6 +132,7 @@ class TestingSupportController { this.scheduledTaskRunner = scheduledTaskRunner; this.azureFinalStorageService = azureFinalStorageService; this.migrationRecordRepository = migrationRecordRepository; + this.portalAccessRepository = portalAccessRepository; } @PostMapping(path = "/create-region", produces = MediaType.APPLICATION_JSON_VALUE) @@ -335,6 +341,29 @@ public ResponseEntity> createAuthenticatedUser(@PathVariable )); } + @PostMapping("/create-portal-access-without-email-invite/{userId}") + public ResponseEntity> createPortalAccessWithoutEmailInvite(@PathVariable UUID userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new NotFoundException("User: " + userId)); + if (!user.getPortalAccess().isEmpty()) { + PortalAccess currentPortalAccess = user.getPortalAccess().stream() + .filter(access -> !access.isDeleted()) + .findFirst() + .orElse(null); + if (currentPortalAccess != null) { + return ResponseEntity.ok(Map.of("portalAccessId", currentPortalAccess.getId().toString())); + } + } + + PortalAccess portalAccess = new PortalAccess(); + portalAccess.setId(UUID.randomUUID()); + portalAccess.setStatus(AccessStatus.INVITATION_SENT); + portalAccess.setUser(user); + portalAccess.setInvitedAt(Timestamp.from(Instant.now())); + portalAccessRepository.saveAndFlush(portalAccess); + return ResponseEntity.ok(Map.of("portalAccessId", portalAccess.getId().toString())); + } + @PostMapping(value = "/create-ready-to-use-booking/{caseReference}", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity> createReadyToUseBooking(@PathVariable String caseReference) { var cases = caseRepository.findAllByReference(caseReference); diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/CreatePortalAccessDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/CreatePortalAccessDTO.java index d96ab7e18c..2b96a62f79 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/CreatePortalAccessDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/CreatePortalAccessDTO.java @@ -32,4 +32,12 @@ public class CreatePortalAccessDTO { @Schema(description = "PortalAccessRegisteredAt") private Timestamp registeredAt; + + public CreatePortalAccessDTO(PortalAccessDTO portalAccessDTO) { + this.id = portalAccessDTO.getId(); + this.lastAccess = portalAccessDTO.getLastAccess(); + this.status = portalAccessDTO.getStatus(); + this.invitedAt = portalAccessDTO.getInvitedAt(); + this.registeredAt = portalAccessDTO.getRegisteredAt(); + } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/services/PortalAccessService.java b/src/main/java/uk/gov/hmcts/reform/preapi/services/PortalAccessService.java index ba2150bf07..2258f8e77d 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/services/PortalAccessService.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/services/PortalAccessService.java @@ -33,13 +33,14 @@ public UpsertResult update(CreatePortalAccessDTO createDto) { entity.setLastAccess(createDto.getLastAccess()); entity.setStatus(createDto.getStatus()); entity.setInvitedAt(createDto.getInvitedAt()); - entity.setRegisteredAt(createDto.getRegisteredAt()); + entity.setRegisteredAt(createDto.getRegisteredAt() == null && entity.getStatus() == AccessStatus.ACTIVE + ? Timestamp.from(Instant.now()) + : createDto.getRegisteredAt()); portalAccessRepository.save(entity); return UpsertResult.UPDATED; } - @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void deleteById(UUID portalId) { portalAccessRepository diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/dto/CreatePortalAccessDTOTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/dto/CreatePortalAccessDTOTest.java new file mode 100644 index 0000000000..2a0538c825 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/preapi/dto/CreatePortalAccessDTOTest.java @@ -0,0 +1,29 @@ +package uk.gov.hmcts.reform.preapi.dto; + +import org.junit.jupiter.api.Test; +import uk.gov.hmcts.reform.preapi.enums.AccessStatus; + +import java.sql.Timestamp; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CreatePortalAccessDTOTest { + @Test + void constructorPortalAccessDto() { + PortalAccessDTO portalAccessDTO = new PortalAccessDTO(); + portalAccessDTO.setId(UUID.randomUUID()); + portalAccessDTO.setLastAccess(new Timestamp(System.currentTimeMillis() - 100000)); + portalAccessDTO.setStatus(AccessStatus.ACTIVE); + portalAccessDTO.setInvitedAt(new Timestamp(System.currentTimeMillis() - 200000)); + portalAccessDTO.setRegisteredAt(new Timestamp(System.currentTimeMillis() - 300000)); + + CreatePortalAccessDTO createDTO = new CreatePortalAccessDTO(portalAccessDTO); + + assertEquals(portalAccessDTO.getId(), createDTO.getId()); + assertEquals(portalAccessDTO.getLastAccess(), createDTO.getLastAccess()); + assertEquals(portalAccessDTO.getStatus(), createDTO.getStatus()); + assertEquals(portalAccessDTO.getInvitedAt(), createDTO.getInvitedAt()); + assertEquals(portalAccessDTO.getRegisteredAt(), createDTO.getRegisteredAt()); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/services/PortalAccessServiceTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/services/PortalAccessServiceTest.java index 0049fab942..9be05687ed 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/services/PortalAccessServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/services/PortalAccessServiceTest.java @@ -94,4 +94,56 @@ void deleteByIdSuccess() { verify(portalAccessRepository, times(1)).save(any()); } + @Test + @DisplayName("Update should set registeredAt when status is active and registeredAt is null") + public void updateShouldSetRegisteredAtWhenStatusIsActiveAndRegisteredAtIsNull() { + CreatePortalAccessDTO model = new CreatePortalAccessDTO(); + model.setId(UUID.randomUUID()); + model.setStatus(AccessStatus.ACTIVE); + model.setLastAccess(Timestamp.from(Instant.now())); + model.setInvitedAt(Timestamp.from(Instant.now())); + model.setRegisteredAt(null); + + PortalAccess entity = new PortalAccess(); + entity.setId(model.getId()); + entity.setRegisteredAt(null); + entity.setStatus(AccessStatus.INACTIVE); + + when(portalAccessRepository.findByIdAndDeletedAtIsNull(model.getId())) + .thenReturn(Optional.of(entity)); + + assertThat(portalAccessService.update(model)).isEqualTo(UpsertResult.UPDATED); + + verify(portalAccessRepository, times(1)).findByIdAndDeletedAtIsNull(model.getId()); + verify(portalAccessRepository, times(1)).save(any()); + + assertThat(entity.getRegisteredAt()).isNotNull(); + } + + @Test + @DisplayName("Update should not override registeredAt when already set") + public void updateShouldNotOverrideRegisteredAtWhenAlreadySet() { + Timestamp existingRegisteredAt = Timestamp.from(Instant.now()); + CreatePortalAccessDTO model = new CreatePortalAccessDTO(); + model.setId(UUID.randomUUID()); + model.setStatus(AccessStatus.INACTIVE); + model.setLastAccess(Timestamp.from(Instant.now())); + model.setInvitedAt(Timestamp.from(Instant.now())); + model.setRegisteredAt(existingRegisteredAt); + + PortalAccess entity = new PortalAccess(); + entity.setId(model.getId()); + entity.setRegisteredAt(existingRegisteredAt); + entity.setStatus(AccessStatus.ACTIVE); + + when(portalAccessRepository.findByIdAndDeletedAtIsNull(model.getId())) + .thenReturn(Optional.of(entity)); + + assertThat(portalAccessService.update(model)).isEqualTo(UpsertResult.UPDATED); + + verify(portalAccessRepository, times(1)).findByIdAndDeletedAtIsNull(model.getId()); + verify(portalAccessRepository, times(1)).save(any()); + + assertThat(entity.getRegisteredAt()).isEqualTo(model.getRegisteredAt()); + } }