Skip to content
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -335,6 +341,29 @@ public ResponseEntity<Map<String, String>> createAuthenticatedUser(@PathVariable
));
}

@PostMapping("/create-portal-access-without-email-invite/{userId}")
public ResponseEntity<Map<String, String>> 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<Map<String, String>> createReadyToUseBooking(@PathVariable String caseReference) {
var cases = caseRepository.findAllByReference(caseReference);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
}