diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/AuditControllerFT.java b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/AuditControllerFT.java index ad9a8a85a0..9fb588b8c9 100644 --- a/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/AuditControllerFT.java +++ b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/AuditControllerFT.java @@ -18,9 +18,8 @@ import static org.assertj.core.api.Assertions.assertThat; public class AuditControllerFT extends FunctionalTestBase { - - @DisplayName("Should fail to update an audit record as they are immutable") @Test + @DisplayName("Should fail to update an audit record as they are immutable") void updateAuditFailure() throws JsonProcessingException { var audit = new CreateAuditDTO(); audit.setId(UUID.randomUUID()); @@ -36,16 +35,8 @@ void updateAuditFailure() throws JsonProcessingException { .isEqualTo("Data is immutable and cannot be changed. Id: " + audit.getId()); } - private Response putAudit(CreateAuditDTO dto) throws JsonProcessingException { - return doPutRequest( - AUDIT_ENDPOINT + dto.getId(), - OBJECT_MAPPER.writeValueAsString(dto), - TestingSupportRoles.SUPER_USER - ); - } - - @DisplayName("Should sort by created at desc") @Test + @DisplayName("Should sort by created at desc") void getAuditLogsSortBy() throws JsonProcessingException { var audit1 = new CreateAuditDTO(); audit1.setId(UUID.randomUUID()); @@ -75,8 +66,8 @@ void getAuditLogsSortBy() throws JsonProcessingException { assertThat(auditLogs1.get(2).getCreatedAt()).isAfter(auditLogs1.getLast().getCreatedAt()); } - @ParameterizedTest @NullSource + @ParameterizedTest @EnumSource(value = TestingSupportRoles.class, names = "SUPER_USER", mode = EnumSource.Mode.EXCLUDE) @DisplayName("Unauthorised use of endpoints should return 403 (or 401 for null authorisation)") void unauthorisedRequestsReturn403Or401(TestingSupportRoles testingSupportRole) throws JsonProcessingException { @@ -87,4 +78,29 @@ void unauthorisedRequestsReturn403Or401(TestingSupportRoles testingSupportRole) assertResponseCode(getAuditLogsResponse, 401); } } + + @Test + @DisplayName("Should return 404 when trying to get a non-existent audit record") + void getNonExistentAudit() { + var getAuditResponse = doGetRequest(AUDIT_ENDPOINT + UUID.randomUUID(), TestingSupportRoles.SUPER_USER); + assertResponseCode(getAuditResponse, 404); + } + + @Test + @DisplayName("Should return 200 when getting a valid audit record") + void getAuditById() { + var getAudits = doGetRequest("/testing-support/latest-audits", TestingSupportRoles.SUPER_USER); + var auditId = getAudits.jsonPath().getUUID("_embedded.auditList[0].id"); + + var getAuditResponse = doGetRequest(AUDIT_ENDPOINT + auditId, TestingSupportRoles.SUPER_USER); + assertResponseCode(getAuditResponse, 200); + } + + private Response putAudit(CreateAuditDTO dto) throws JsonProcessingException { + return doPutRequest( + AUDIT_ENDPOINT + dto.getId(), + OBJECT_MAPPER.writeValueAsString(dto), + TestingSupportRoles.SUPER_USER + ); + } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/AuditController.java b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/AuditController.java index e707689bd5..421149854f 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/AuditController.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/AuditController.java @@ -17,18 +17,23 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import uk.gov.hmcts.reform.preapi.controllers.params.SearchAudits; import uk.gov.hmcts.reform.preapi.dto.AuditDTO; import uk.gov.hmcts.reform.preapi.dto.CreateAuditDTO; +import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; import uk.gov.hmcts.reform.preapi.exception.PathPayloadMismatchException; import uk.gov.hmcts.reform.preapi.exception.RequestedPageOutOfRangeException; import uk.gov.hmcts.reform.preapi.services.AuditService; +import java.sql.Timestamp; +import java.time.LocalDateTime; import java.util.UUID; import static uk.gov.hmcts.reform.preapi.config.OpenAPIConfiguration.X_USER_ID_HEADER; @@ -61,8 +66,55 @@ public ResponseEntity upsertAudit(@RequestHeader HttpHeaders headers, return new ResponseEntity<>(HttpStatus.CREATED); } + @GetMapping("/{id}") + @PreAuthorize("hasAnyRole('ROLE_SUPER_USER')") + @Operation(operationId = "getAudit", summary = "Get an Audit Entry") + public HttpEntity getAuditById(@PathVariable UUID id) { + return ResponseEntity.ok(auditService.findById(id)); + } + @GetMapping @Operation(operationId = "getAuditLogs", summary = "Search all Audits") + @Parameter( + name = "after", + description = "The date time to search after", + schema = @Schema(implementation = LocalDateTime.class, format = "iso-date-time"), + example = "2021-01-01T00:00:00" + ) + @Parameter( + name = "before", + description = "The date time to search before", + schema = @Schema(implementation = LocalDateTime.class, format = "iso-date-time"), + example = "2021-01-01T00:00:00" + ) + @Parameter( + name = "functionalArea", + description = "The functional area to search by", + schema = @Schema(implementation = String.class), + example = "API" + ) + @Parameter( + name = "source", + description = "The source to search by", + schema = @Schema(implementation = AuditLogSource.class) + ) + @Parameter( + name = "userName", + description = "Partial user's name to search by", + schema = @Schema(implementation = String.class) + ) + @Parameter( + name = "courtId", + description = "The court id of the audit to search by", + schema = @Schema(implementation = UUID.class), + example = "123e4567-e89b-12d3-a456-426614174000" + ) + @Parameter( + name = "caseReference", + description = "The case reference of the audit to search by", + schema = @Schema(implementation = String.class), + example = "CASE12345" + ) @Parameter( name = "page", description = "The page number of search result to return", @@ -77,6 +129,7 @@ public ResponseEntity upsertAudit(@RequestHeader HttpHeaders headers, ) @PreAuthorize("hasAnyRole('ROLE_SUPER_USER')") public HttpEntity>> searchAuditLogs( + @Parameter(hidden = true) @ModelAttribute SearchAudits params, @SortDefault.SortDefaults( @SortDefault(sort = "createdAt", direction = Sort.Direction.DESC) ) @@ -84,6 +137,13 @@ public HttpEntity>> searchAuditLogs( @Parameter(hidden = true) PagedResourcesAssembler assembler ) { var resultPage = auditService.findAll( + params.getAfter() != null ? Timestamp.valueOf(params.getAfter()) : null, + params.getBefore() != null ? Timestamp.valueOf(params.getBefore()) : null, + params.getFunctionalArea(), + params.getSource(), + params.getUserName(), + params.getCourtId(), + params.getCaseReference(), pageable ); 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 ce6c01550f..c080968184 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 @@ -410,7 +410,7 @@ public HttpEntity>> getLatestAudits( @Parameter(hidden = true) Pageable pageable, @Parameter(hidden = true) PagedResourcesAssembler assembler ) { - var resultPage = auditRepository.searchAll(pageable); + var resultPage = auditRepository.searchAll(null, null, null, null, null, null, null, pageable); if (pageable.getPageNumber() > resultPage.getTotalPages()) { throw new RequestedPageOutOfRangeException(pageable.getPageNumber(), resultPage.getTotalPages()); diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/controllers/params/SearchAudits.java b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/params/SearchAudits.java new file mode 100644 index 0000000000..9889b10320 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/preapi/controllers/params/SearchAudits.java @@ -0,0 +1,40 @@ +package uk.gov.hmcts.reform.preapi.controllers.params; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; +import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Data +public class SearchAudits { + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime after; + + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) + private LocalDateTime before; + + private String functionalArea; + + private AuditLogSource source; + + private String userName; + + private UUID courtId; + + private String caseReference; + + public String getFunctionalArea() { + return functionalArea != null && !functionalArea.isEmpty() ? functionalArea : null; + } + + public String getUserName() { + return userName != null && !userName.isEmpty() ? userName : null; + } + + public String getCaseReference() { + return caseReference != null && !caseReference.isEmpty() ? caseReference : null; + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/dto/AuditDTO.java b/src/main/java/uk/gov/hmcts/reform/preapi/dto/AuditDTO.java index 4c7c171f92..85e026b7fb 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/dto/AuditDTO.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/dto/AuditDTO.java @@ -7,37 +7,46 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; +import uk.gov.hmcts.reform.preapi.dto.base.BaseUserDTO; import uk.gov.hmcts.reform.preapi.entities.Audit; +import uk.gov.hmcts.reform.preapi.entities.User; import java.sql.Timestamp; -import java.util.UUID; @Data @NoArgsConstructor -@EqualsAndHashCode(callSuper = true) @Schema(description = "AuditDTO") +@EqualsAndHashCode(callSuper = true) @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) public class AuditDTO extends CreateAuditDTO { - @Schema(description = "AuditCreatedAt") @NotNull + @Schema(description = "AuditCreatedAt") private Timestamp createdAt; - @Schema(description = "AuditCreatedBy") @NotNull - private UUID createdBy; + @Schema(description = "AuditCreatedBy") + private BaseUserDTO createdBy; public AuditDTO(Audit auditEntity) { super(); - this.id = auditEntity.getId(); - this.tableName = auditEntity.getTableName(); - this.tableRecordId = auditEntity.getTableRecordId(); - this.source = auditEntity.getSource(); - this.category = auditEntity.getCategory(); - this.activity = auditEntity.getActivity(); - this.functionalArea = auditEntity.getFunctionalArea(); - this.auditDetails = auditEntity.getAuditDetails(); - this.createdAt = auditEntity.getCreatedAt(); - this.createdBy = auditEntity.getCreatedBy(); + id = auditEntity.getId(); + tableName = auditEntity.getTableName(); + tableRecordId = auditEntity.getTableRecordId(); + source = auditEntity.getSource(); + category = auditEntity.getCategory(); + activity = auditEntity.getActivity(); + functionalArea = auditEntity.getFunctionalArea(); + auditDetails = auditEntity.getAuditDetails(); + createdAt = auditEntity.getCreatedAt(); + if (auditEntity.getCreatedBy() != null) { + createdBy = new BaseUserDTO(); + createdBy.setId(auditEntity.getCreatedBy()); + } + } + + public AuditDTO(Audit auditEntity, User user) { + this(auditEntity); + createdBy = new BaseUserDTO(user); } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/repositories/AuditRepository.java b/src/main/java/uk/gov/hmcts/reform/preapi/repositories/AuditRepository.java index 998af0b90d..7a9906959b 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/repositories/AuditRepository.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/repositories/AuditRepository.java @@ -4,10 +4,12 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import uk.gov.hmcts.reform.preapi.entities.Audit; import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; +import java.sql.Timestamp; import java.util.List; import java.util.UUID; @@ -29,8 +31,63 @@ AND CAST(FUNCTION('jsonb_extract_path_text', a.auditDetails, 'description') as t @Query( """ SELECT a FROM Audit a + WHERE (CAST(:after as Timestamp) IS NULL + OR CAST(FUNCTION('TIMEZONE', 'Europe/London', a.createdAt) as Timestamp) >= :after) + AND (CAST(:before as Timestamp) IS NULL + OR CAST(FUNCTION('TIMEZONE', 'Europe/London', a.createdAt) as Timestamp) <= :before) + AND (:functionalArea IS NULL OR a.functionalArea ILIKE %:functionalArea%) + AND (CAST(:source as text) IS NULL OR a.source = :source) + AND (:userName IS NULL + OR EXISTS ( + SELECT 1 FROM AppAccess aa + WHERE aa.id = a.createdBy + AND CONCAT(aa.user.firstName, ' ', aa.user.lastName) ILIKE %:userName%) + OR EXISTS ( + SELECT 1 FROM PortalAccess pa + WHERE pa.id = a.createdBy + AND CONCAT(pa.user.firstName, ' ', pa.user.lastName) ILIKE %:userName%)) + AND ((CAST(:courtId as uuid) IS NULL AND :caseReference IS NULL) + OR EXISTS ( + SELECT 1 FROM Court court + WHERE :caseReference IS NULL + AND court.id = a.tableRecordId + AND (CAST(:courtId as uuid) IS NULL OR court.id = :courtId)) + OR EXISTS ( + SELECT 1 FROM Case c + WHERE c.id = a.tableRecordId + AND (CAST(:courtId as uuid) IS NULL OR c.court.id = :courtId) + AND (:caseReference IS NULL OR c.reference ILIKE %:caseReference%)) + OR EXISTS ( + SELECT 1 FROM Booking b + WHERE b.id = a.tableRecordId + AND (CAST(:courtId as uuid) IS NULL OR b.caseId.court.id = :courtId) + AND (:caseReference IS NULL OR b.caseId.reference ILIKE %:caseReference%)) + OR EXISTS ( + SELECT 1 FROM CaptureSession cs + WHERE cs.id = a.tableRecordId + AND (CAST(:courtId as uuid) IS NULL OR cs.booking.caseId.court.id = :courtId) + AND (:caseReference IS NULL OR cs.booking.caseId.reference ILIKE %:caseReference%)) + OR EXISTS ( + SELECT 1 FROM Recording r + WHERE r.id = a.tableRecordId + AND (CAST(:courtId as uuid) IS NULL OR r.captureSession.booking.caseId.court.id = :courtId) + AND (:caseReference IS NULL OR r.captureSession.booking.caseId.reference ILIKE %:caseReference%)) + OR EXISTS ( + SELECT 1 FROM Participant p + WHERE p.id = a.tableRecordId + AND (CAST(:courtId as uuid) IS NULL OR p.caseId.court.id = :courtId) + AND (:caseReference IS NULL OR p.caseId.reference ILIKE %:caseReference%))) ORDER BY a.createdAt DESC """ ) - Page searchAll(Pageable pageable); + Page searchAll( + @Param("after") Timestamp after, + @Param("before") Timestamp before, + @Param("functionalArea") String functionalArea, + @Param("source") AuditLogSource source, + @Param("userName") String userName, + @Param("courtId") UUID courtId, + @Param("caseReference") String caseReference, + Pageable pageable + ); } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/services/AuditService.java b/src/main/java/uk/gov/hmcts/reform/preapi/services/AuditService.java index e26db39d5d..5f90d74bfd 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/services/AuditService.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/services/AuditService.java @@ -8,10 +8,15 @@ import uk.gov.hmcts.reform.preapi.dto.AuditDTO; import uk.gov.hmcts.reform.preapi.dto.CreateAuditDTO; import uk.gov.hmcts.reform.preapi.entities.Audit; +import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; import uk.gov.hmcts.reform.preapi.enums.UpsertResult; import uk.gov.hmcts.reform.preapi.exception.ImmutableDataException; +import uk.gov.hmcts.reform.preapi.exception.NotFoundException; +import uk.gov.hmcts.reform.preapi.repositories.AppAccessRepository; import uk.gov.hmcts.reform.preapi.repositories.AuditRepository; +import uk.gov.hmcts.reform.preapi.repositories.PortalAccessRepository; +import java.sql.Timestamp; import java.util.List; import java.util.UUID; import javax.annotation.Nullable; @@ -20,12 +25,26 @@ public class AuditService { private final AuditRepository auditRepository; + private final AppAccessRepository appAccessRepository; + private final PortalAccessRepository portalAccessRepository; @Autowired - public AuditService(AuditRepository auditRepository) { + public AuditService(AuditRepository auditRepository, + AppAccessRepository appAccessRepository, + PortalAccessRepository portalAccessRepository) { this.auditRepository = auditRepository; + this.appAccessRepository = appAccessRepository; + this.portalAccessRepository = portalAccessRepository; } + @Transactional + public AuditDTO findById(UUID id) { + return auditRepository.findById(id) + .map(this::toDto) + .orElseThrow(() -> new NotFoundException("Audit: " + id)); + } + + @Transactional public UpsertResult upsert(CreateAuditDTO createAuditDTO, @Nullable UUID createdBy) { if (auditRepository.existsById(createAuditDTO.getId())) { throw new ImmutableDataException(createAuditDTO.getId().toString()); @@ -48,13 +67,33 @@ public UpsertResult upsert(CreateAuditDTO createAuditDTO, @Nullable UUID created } @Transactional - public Page findAll(Pageable pageable) { + public Page findAll( + Timestamp after, + Timestamp before, + String functionalArea, + AuditLogSource source, + String userName, + UUID courtId, + String caseReference, + Pageable pageable + ) { return auditRepository - .searchAll(pageable) - .map(AuditDTO::new); + .searchAll(after, before, functionalArea, source, userName, courtId, caseReference, pageable) + .map(this::toDto); } public List getAuditsByTableRecordId(UUID tableRecordId) { return auditRepository.findByTableRecordId(tableRecordId); } + + private AuditDTO toDto(Audit audit) { + var createdById = audit.getCreatedBy(); + return createdById == null + ? new AuditDTO(audit) + : appAccessRepository.findById(createdById) + .map(aa -> new AuditDTO(audit, aa.getUser())) + .orElseGet(() -> portalAccessRepository.findById(createdById) + .map(pa -> new AuditDTO(audit, pa.getUser())) + .orElseGet(() -> new AuditDTO(audit))); + } } diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/controller/AuditControllerTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/controller/AuditControllerTest.java index 2ce2a13209..339ccd2f9d 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/controller/AuditControllerTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/controller/AuditControllerTest.java @@ -4,6 +4,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -11,23 +12,26 @@ import org.springframework.data.domain.PageImpl; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.MvcResult; import uk.gov.hmcts.reform.preapi.controllers.AuditController; import uk.gov.hmcts.reform.preapi.dto.AuditDTO; import uk.gov.hmcts.reform.preapi.dto.CreateAuditDTO; import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; import uk.gov.hmcts.reform.preapi.enums.UpsertResult; import uk.gov.hmcts.reform.preapi.exception.ImmutableDataException; +import uk.gov.hmcts.reform.preapi.exception.NotFoundException; import uk.gov.hmcts.reform.preapi.security.service.UserAuthenticationService; import uk.gov.hmcts.reform.preapi.services.AuditService; import uk.gov.hmcts.reform.preapi.services.ScheduledTaskRunner; +import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.List; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -38,7 +42,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static uk.gov.hmcts.reform.preapi.config.OpenAPIConfiguration.X_USER_ID_HEADER; - @WebMvcTest(AuditController.class) @AutoConfigureMockMvc(addFilters = false) @SuppressWarnings({"PMD.LinguisticNaming"}) @@ -58,15 +61,18 @@ class AuditControllerTest { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + private String getPath(UUID auditId) { + return "/audit/" + auditId; + } + @BeforeAll - static void setUp() { + public static void setUp() { OBJECT_MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SS'Z'")); } - @DisplayName("Should create an audit record with 201 response code") @Test - void createAuditEndpointCreated() throws Exception { - + @DisplayName("Should create an audit record with 201 response code") + public void createAuditEndpointCreated() throws Exception { var audit = new CreateAuditDTO(); audit.setId(UUID.randomUUID()); audit.setAuditDetails(OBJECT_MAPPER.readTree("{\"test\": \"test\"}")); @@ -75,9 +81,7 @@ void createAuditEndpointCreated() throws Exception { var xUserId = UUID.randomUUID(); when(auditService.upsert(audit, xUserId)).thenReturn(UpsertResult.CREATED); - System.out.println(OBJECT_MAPPER.writeValueAsString(audit)); - - MvcResult response = mockMvc.perform(put(getPath(audit.getId())) + var response = mockMvc.perform(put(getPath(audit.getId())) .with(csrf()) .header(X_USER_ID_HEADER, xUserId) .content(OBJECT_MAPPER.writeValueAsString(audit)) @@ -89,10 +93,9 @@ void createAuditEndpointCreated() throws Exception { assertThat(response.getResponse().getContentAsString()).isEqualTo(""); } - @DisplayName("Should create an audit record with 201 response code without x-user-id header") @Test - void createAuditEndpointWithoutXUserIdCreated() throws Exception { - + @DisplayName("Should create an audit record with 201 response code without x-user-id header") + public void createAuditEndpointWithoutXUserIdCreated() throws Exception { var audit = new CreateAuditDTO(); audit.setId(UUID.randomUUID()); audit.setAuditDetails(OBJECT_MAPPER.readTree("{\"test\": \"test\"}")); @@ -101,9 +104,7 @@ void createAuditEndpointWithoutXUserIdCreated() throws Exception { var xUserId = UUID.randomUUID(); when(auditService.upsert(audit, xUserId)).thenReturn(UpsertResult.CREATED); - System.out.println(OBJECT_MAPPER.writeValueAsString(audit)); - - MvcResult response = mockMvc.perform(put(getPath(audit.getId())) + var response = mockMvc.perform(put(getPath(audit.getId())) .with(csrf()) .content(OBJECT_MAPPER.writeValueAsString(audit)) .contentType(MediaType.APPLICATION_JSON_VALUE) @@ -114,10 +115,9 @@ void createAuditEndpointWithoutXUserIdCreated() throws Exception { assertThat(response.getResponse().getContentAsString()).isEqualTo(""); } - @DisplayName("Should fail to update an audit record as they are immutable") @Test - void updateAuditFailure() throws Exception { - + @DisplayName("Should fail to update an audit record as they are immutable") + public void updateAuditFailure() throws Exception { var audit = new CreateAuditDTO(); audit.setId(UUID.randomUUID()); audit.setAuditDetails(OBJECT_MAPPER.readTree("{\"test\": \"test\"}")); @@ -137,10 +137,9 @@ void updateAuditFailure() throws Exception { .value("Data is immutable and cannot be changed. Id: " + audit.getId())); } - @DisplayName("Should fail to create an audit record with 400 response code auditId mismatch") @Test - void createAuditEndpointAuditIdMismatch() throws Exception { - + @DisplayName("Should fail to create an audit record with 400 response code auditId mismatch") + public void createAuditEndpointAuditIdMismatch() throws Exception { var audit = new CreateAuditDTO(); audit.setId(UUID.randomUUID()); audit.setAuditDetails(OBJECT_MAPPER.readTree("{\"test\": \"test\"}")); @@ -148,7 +147,7 @@ void createAuditEndpointAuditIdMismatch() throws Exception { var xUserId = UUID.randomUUID(); - MvcResult response = mockMvc.perform(put(getPath(UUID.randomUUID())) + var response = mockMvc.perform(put(getPath(UUID.randomUUID())) .with(csrf()) .header(X_USER_ID_HEADER, xUserId) .content(OBJECT_MAPPER.writeValueAsString(audit)) @@ -161,26 +160,21 @@ void createAuditEndpointAuditIdMismatch() throws Exception { .isEqualTo("{\"message\":\"Path id does not match payload property createAuditDTO.id\"}"); } - @DisplayName("Should fail to create an audit record with 400 response code") @Test - void createAuditEndpointNotAcceptable() throws Exception { - + @DisplayName("Should fail to create an audit record with 400 response code") + public void createAuditEndpointNotAcceptable() throws Exception { mockMvc.perform(put(getPath(UUID.randomUUID()))) .andExpect(status().is4xxClientError()); } - private String getPath(UUID auditId) { - return "/audit/" + auditId; - } - - @DisplayName("Should get a list of audit logs with 200 response code") @Test - void testSearchAuditLogsSuccess() throws Exception { - UUID auditLogId = UUID.randomUUID(); + @DisplayName("Should get a list of audit logs with 200 response code") + public void testSearchAuditLogsSuccess() throws Exception { + var auditLogId = UUID.randomUUID(); var mockAuditDTO = new AuditDTO(); mockAuditDTO.setId(auditLogId); var auditDTOList = List.of(mockAuditDTO); - when(auditService.findAll(any())) + when(auditService.findAll(any(), any(), any(), any(), any(), any(), any(), any())) .thenReturn(new PageImpl<>(auditDTOList)); mockMvc.perform(get("/audit") @@ -190,22 +184,205 @@ void testSearchAuditLogsSuccess() throws Exception { .andExpect(jsonPath("$._embedded.auditDTOList").isNotEmpty()) .andExpect(jsonPath("$._embedded.auditDTOList[0].id").value(auditLogId.toString())); - verify(auditService, times(1)).findAll(any()); + verify(auditService, times(1)).findAll(any(), any(), any(), any(), any(), any(), any(), any()); + } + + @Test + @DisplayName("Search audits by created after") + public void searchLogsByCreatedAfter() throws Exception { + var auditLogId = UUID.randomUUID(); + var mockAudit = new AuditDTO(); + mockAudit.setId(auditLogId); + var auditDTOList = List.of(mockAudit); + var searchTimestampAfterStr = "2021-01-01T00:00:00"; + when(auditService.findAll(any(Timestamp.class), any(), any(), any(), any(), any(), any(), any())) + .thenReturn(new PageImpl<>(auditDTOList)); + + mockMvc.perform(get("/audit?after=" + searchTimestampAfterStr) + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.auditDTOList").isNotEmpty()) + .andExpect(jsonPath("$._embedded.auditDTOList[0].id").value(auditLogId.toString())); + + var argumentCaptor = ArgumentCaptor.forClass(Timestamp.class); + + verify(auditService, times(1)) + .findAll(argumentCaptor.capture(), any(), any(), any(), any(), any(), any(), any()); + + assertThat(argumentCaptor.getValue().toString()).isEqualTo("2021-01-01 00:00:00.0"); + } + + @Test + @DisplayName("Search audits by created before") + public void searchLogsByCreatedBefore() throws Exception { + var auditLogId = UUID.randomUUID(); + var mockAudit = new AuditDTO(); + mockAudit.setId(auditLogId); + var auditDTOList = List.of(mockAudit); + var searchTimestampBeforeStr = "2021-01-01T00:00:00"; + when(auditService.findAll(any(), any(Timestamp.class), any(), any(), any(), any(), any(), any())) + .thenReturn(new PageImpl<>(auditDTOList)); + + mockMvc.perform(get("/audit?before=" + searchTimestampBeforeStr) + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.auditDTOList").isNotEmpty()) + .andExpect(jsonPath("$._embedded.auditDTOList[0].id").value(auditLogId.toString())); + + var argumentCaptor = ArgumentCaptor.forClass(Timestamp.class); + + verify(auditService, times(1)) + .findAll(any(), argumentCaptor.capture(), any(), any(), any(), any(), any(), any()); + + assertThat(argumentCaptor.getValue().toString()).isEqualTo("2021-01-01 00:00:00.0"); } - @DisplayName("Requested page out of range") @Test - void getAuditLogsRequestedPageOutOfRange() throws Exception { + @DisplayName("Search audits by functional area") + public void searchLogsByFunctionalArea() throws Exception { + var auditLogId = UUID.randomUUID(); + var mockAudit = new AuditDTO(); + mockAudit.setId(auditLogId); + var auditDTOList = List.of(mockAudit); + var functionalArea = "API"; + when(auditService.findAll(any(), any(), eq(functionalArea), any(), any(), any(), any(), any())) + .thenReturn(new PageImpl<>(auditDTOList)); + + mockMvc.perform(get("/audit?functionalArea=" + functionalArea) + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.auditDTOList").isNotEmpty()) + .andExpect(jsonPath("$._embedded.auditDTOList[0].id").value(auditLogId.toString())); + + verify(auditService, times(1)) + .findAll(any(), any(), eq(functionalArea), any(), any(), any(), any(), any()); + } + + @Test + @DisplayName("Search audits by source") + public void searchLogsBySource() throws Exception { + var auditLogId = UUID.randomUUID(); + var mockAudit = new AuditDTO(); + mockAudit.setId(auditLogId); + var auditDTOList = List.of(mockAudit); + var source = AuditLogSource.AUTO; + when(auditService.findAll(any(), any(), any(), eq(source), any(), any(), any(), any())) + .thenReturn(new PageImpl<>(auditDTOList)); + + mockMvc.perform(get("/audit?source=" + source) + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.auditDTOList").isNotEmpty()) + .andExpect(jsonPath("$._embedded.auditDTOList[0].id").value(auditLogId.toString())); + + verify(auditService, times(1)) + .findAll(any(), any(), any(), eq(source), any(), any(), any(), any()); + } + + @Test + @DisplayName("Search audits by user's name") + public void searchLogsByUserName() throws Exception { + var auditLogId = UUID.randomUUID(); + var mockAudit = new AuditDTO(); + mockAudit.setId(auditLogId); + var auditDTOList = List.of(mockAudit); + var name = "someone"; + when(auditService.findAll(any(), any(), any(), any(), eq(name), any(), any(), any())) + .thenReturn(new PageImpl<>(auditDTOList)); + + mockMvc.perform(get("/audit?userName=" + name) + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.auditDTOList").isNotEmpty()) + .andExpect(jsonPath("$._embedded.auditDTOList[0].id").value(auditLogId.toString())); + + verify(auditService, times(1)) + .findAll(any(), any(), any(), any(), eq(name), any(), any(), any()); + } + + @Test + @DisplayName("Search audits by court id") + public void searchLogsByCourtId() throws Exception { + var auditLogId = UUID.randomUUID(); + var mockAudit = new AuditDTO(); + mockAudit.setId(auditLogId); + var auditDTOList = List.of(mockAudit); + var courtId = UUID.randomUUID(); + when(auditService.findAll(any(), any(), any(), any(), any(), eq(courtId), any(), any())) + .thenReturn(new PageImpl<>(auditDTOList)); + + mockMvc.perform(get("/audit?courtId=" + courtId) + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.auditDTOList").isNotEmpty()) + .andExpect(jsonPath("$._embedded.auditDTOList[0].id").value(auditLogId.toString())); + + verify(auditService, times(1)) + .findAll(any(), any(), any(), any(), any(), eq(courtId), any(), any()); + } + + @Test + @DisplayName("Search audits by case reference") + public void searchLogsByCaseReference() throws Exception { + var auditLogId = UUID.randomUUID(); + var mockAudit = new AuditDTO(); + mockAudit.setId(auditLogId); + var auditDTOList = List.of(mockAudit); + var caseReference = "CASE123"; + when(auditService.findAll(any(), any(), any(), any(), any(), any(), eq(caseReference), any())) + .thenReturn(new PageImpl<>(auditDTOList)); + + mockMvc.perform(get("/audit?caseReference=" + caseReference) + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$._embedded.auditDTOList").isNotEmpty()) + .andExpect(jsonPath("$._embedded.auditDTOList[0].id").value(auditLogId.toString())); + + verify(auditService, times(1)) + .findAll(any(), any(), any(), any(), any(), any(), eq(caseReference), any()); + } + + @Test + @DisplayName("Requested page out of range") + public void getAuditLogsRequestedPageOutOfRange() throws Exception { UUID auditLogId = UUID.randomUUID(); var mockAuditDTO = new AuditDTO(); mockAuditDTO.setId(auditLogId); var auditDTOList = List.of(mockAuditDTO); - when(auditService.findAll(any())) + when(auditService.findAll(any(), any(), any(), any(), any(), any(), any(), any())) .thenReturn(new PageImpl<>(auditDTOList)); mockMvc.perform(get("/audit?page=5")) .andExpect(status().isBadRequest()) .andExpect(jsonPath("$.message").value("Requested page {5} is out of range. Max page is {1}")); + } + @Test + @DisplayName("Should get audit by id") + public void getAuditByIdSuccess() throws Exception { + UUID auditLogId = UUID.randomUUID(); + var mockAuditDTO = new AuditDTO(); + mockAuditDTO.setId(auditLogId); + when(auditService.findById(auditLogId)).thenReturn(mockAuditDTO); + + mockMvc.perform(get("/audit/" + auditLogId) + .with(csrf())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(auditLogId.toString())); + } + + @Test + @DisplayName("Should return 404 when audit not found") + public void getAuditByIdNotFound() throws Exception { + UUID auditLogId = UUID.randomUUID(); + var mockAuditDTO = new AuditDTO(); + mockAuditDTO.setId(auditLogId); + doThrow(new NotFoundException("Audit: " + auditLogId)).when(auditService).findById(auditLogId); + + mockMvc.perform(get("/audit/" + auditLogId) + .with(csrf())) + .andExpect(status().isNotFound()) + .andExpect(jsonPath("$.message") + .value("Not found: Audit: " + auditLogId)); } } diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/controllers/params/SearchAuditsTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/controllers/params/SearchAuditsTest.java new file mode 100644 index 0000000000..5b25425408 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/preapi/controllers/params/SearchAuditsTest.java @@ -0,0 +1,71 @@ +package uk.gov.hmcts.reform.preapi.controllers.params; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class SearchAuditsTest { + + private SearchAudits search; + + @BeforeEach + void setUp() { + search = new SearchAudits(); + } + + @Test + void testGetFunctionalArea_WhenNotNullOrEmpty() { + search.setFunctionalArea("FunctionalArea"); + assertEquals("FunctionalArea", search.getFunctionalArea()); + } + + @Test + void testGetFunctionalArea_WhenNull() { + search.setFunctionalArea(null); + assertNull(search.getFunctionalArea()); + } + + @Test + void testGetFunctionalArea_WhenEmpty() { + search.setFunctionalArea(""); + assertNull(search.getFunctionalArea()); + } + + @Test + void testGetUserName_WhenNotNullOrEmpty() { + search.setUserName("Example Person"); + assertEquals("Example Person", search.getUserName()); + } + + @Test + void testGetUserName_WhenNull() { + search.setUserName(null); + assertNull(search.getUserName()); + } + + @Test + void testGetUserName_WhenEmpty() { + search.setUserName(""); + assertNull(search.getUserName()); + } + + @Test + void testGetCaseReference_WhenNotNullOrEmpty() { + search.setCaseReference("CASE12345"); + assertEquals("CASE12345", search.getCaseReference()); + } + + @Test + void testGetCaseReference_WhenNull() { + search.setCaseReference(null); + assertNull(search.getCaseReference()); + } + + @Test + void testGetCaseReference_WhenEmpty() { + search.setCaseReference(""); + assertNull(search.getCaseReference()); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/dto/AuditDTOTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/dto/AuditDTOTest.java index 0333b4d8af..4825325cd8 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/dto/AuditDTOTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/dto/AuditDTOTest.java @@ -6,6 +6,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import uk.gov.hmcts.reform.preapi.entities.Audit; +import uk.gov.hmcts.reform.preapi.entities.User; import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; import java.sql.Timestamp; @@ -33,14 +34,38 @@ static void setUp() throws JsonProcessingException { auditEntity.setCreatedAt(new Timestamp(System.currentTimeMillis())); } - @DisplayName("Should create an Audit model from an Audit entity") @Test + @DisplayName("Should create an Audit model from an Audit entity") void createCaseFromEntity() { + assertAuditDtoValid(new AuditDTO(auditEntity)); + } + + @Test + @DisplayName("Should create an Audit model from an Audit and User entity") + public void createCaseFromAuditAndUser() { + var user = new User(); + user.setId(auditEntity.getCreatedBy()); + user.setFirstName("Example"); + user.setLastName("Person"); + user.setEmail("example@example.com"); + user.setPhone("1234567890"); + user.setOrganisation("Example Organisation"); + + var model = new AuditDTO(auditEntity, user); + assertAuditDtoValid(model); + + var userDto = model.getCreatedBy(); + assertThat(userDto.getFirstName()).isEqualTo(user.getFirstName()); + assertThat(userDto.getLastName()).isEqualTo(user.getLastName()); + assertThat(userDto.getEmail()).isEqualTo(user.getEmail()); + assertThat(userDto.getPhoneNumber()).isEqualTo(user.getPhone()); + assertThat(userDto.getOrganisation()).isEqualTo(user.getOrganisation()); + } - var model = new AuditDTO(auditEntity); + private void assertAuditDtoValid(AuditDTO model) { assertThat(model.getId()).isEqualTo(auditEntity.getId()); assertThat(model.getCreatedAt()).isEqualTo(auditEntity.getCreatedAt()); - assertThat(model.getCreatedBy()).isEqualTo(auditEntity.getCreatedBy()); + assertThat(model.getCreatedBy().getId()).isEqualTo(auditEntity.getCreatedBy()); assertThat(model.getAuditDetails().get("foo").asText()).isEqualTo("bar"); assertThat(model.getActivity()).isEqualTo(auditEntity.getActivity()); assertThat(model.getCategory()).isEqualTo(auditEntity.getCategory()); diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/services/AuditServiceTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/services/AuditServiceTest.java index e3a458e7c3..193c9c8006 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/services/AuditServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/services/AuditServiceTest.java @@ -1,20 +1,26 @@ package uk.gov.hmcts.reform.preapi.services; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; import org.springframework.security.core.context.SecurityContextHolder; import uk.gov.hmcts.reform.preapi.dto.CreateAuditDTO; import uk.gov.hmcts.reform.preapi.entities.AppAccess; import uk.gov.hmcts.reform.preapi.entities.Audit; +import uk.gov.hmcts.reform.preapi.entities.PortalAccess; import uk.gov.hmcts.reform.preapi.entities.User; +import uk.gov.hmcts.reform.preapi.enums.AuditLogSource; import uk.gov.hmcts.reform.preapi.enums.UpsertResult; import uk.gov.hmcts.reform.preapi.exception.ImmutableDataException; +import uk.gov.hmcts.reform.preapi.exception.NotFoundException; import uk.gov.hmcts.reform.preapi.repositories.AppAccessRepository; import uk.gov.hmcts.reform.preapi.repositories.AuditRepository; +import uk.gov.hmcts.reform.preapi.repositories.PortalAccessRepository; import uk.gov.hmcts.reform.preapi.security.authentication.UserAuthentication; import java.sql.Timestamp; @@ -27,6 +33,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @SpringBootTest(classes = AuditService.class) @@ -40,11 +49,38 @@ class AuditServiceTest { @MockBean private AppAccessRepository appAccessRepository; + @MockBean + private PortalAccessRepository portalAccessRepository; + @Autowired private AuditService auditService; - @DisplayName("Create an audit entry") + private Timestamp after; + private Timestamp before; + private String functionalArea; + private AuditLogSource source; + private String userName; + private UUID courtId; + private String caseReference; + private Pageable pageable; + private Audit audit; + + @BeforeEach + void setUp() { + after = Timestamp.valueOf("2024-01-01 00:00:00"); + before = Timestamp.valueOf("2024-12-31 23:59:59"); + functionalArea = "API"; + source = AuditLogSource.AUTO; + userName = "testUser"; + courtId = UUID.randomUUID(); + caseReference = "CASE123"; + pageable = mock(Pageable.class); + audit = new Audit(); + audit.setCreatedBy(UUID.randomUUID()); + } + @Test + @DisplayName("Create an audit entry") void upsertAuditSuccessCreated() { var auditModel = new CreateAuditDTO(); auditModel.setId(UUID.randomUUID()); @@ -63,8 +99,8 @@ void upsertAuditSuccessCreated() { assertThat(auditService.upsert(auditModel, appAccess.getId())).isEqualTo(UpsertResult.CREATED); } - @DisplayName("Create an audit entry") @Test + @DisplayName("Create an audit entry") void upsertAuditSuccessWhenIdCannotBeFoundCreated() { var auditModel = new CreateAuditDTO(); auditModel.setId(UUID.randomUUID()); @@ -80,8 +116,8 @@ void upsertAuditSuccessWhenIdCannotBeFoundCreated() { assertThat(auditService.upsert(auditModel, id)).isEqualTo(UpsertResult.CREATED); } - @DisplayName("Create a booking when case not found") @Test + @DisplayName("Create a booking when case not found") void upsertUpdateAuditAttempt() { var auditModel = new CreateAuditDTO(); auditModel.setId(UUID.randomUUID()); @@ -94,22 +130,201 @@ void upsertUpdateAuditAttempt() { ); } - @DisplayName("Find a list of audit logs and return a list of models") @Test + @DisplayName("Find a list of audit logs and return a list of models") void findAllAuditsSuccess() { auditEntity = new Audit(); auditEntity.setId(UUID.randomUUID()); auditEntity.setCreatedAt(Timestamp.from(Instant.now())); when( - auditRepository.searchAll(any()) + auditRepository.searchAll(any(), any(), any(), any(), any(), any(), any(), any()) ).thenReturn(new PageImpl<>(List.of(auditEntity))); var mockAuth = mock(UserAuthentication.class); when(mockAuth.isAdmin()).thenReturn(true); SecurityContextHolder.getContext().setAuthentication(mockAuth); - var modelList = auditService.findAll(null).get().toList(); + var modelList = auditService.findAll(null, null, null, null, null, null, null, null).get().toList(); assertThat(modelList).hasSize(1); assertThat(modelList.getFirst().getId()).isEqualTo(auditEntity.getId()); } + + @Test + @DisplayName("Search audits and return a list of models with created by from app access") + void findAllAuditsFindUserAppAccess() { + when(auditRepository.searchAll( + after, + before, + functionalArea, + source, + userName, + courtId, + caseReference, + pageable + )) + .thenReturn(new PageImpl<>(List.of(audit))); + + var user = new User(); + user.setId(UUID.randomUUID()); + user.setFirstName("test"); + user.setLastName("user"); + user.setEmail("example@example.com"); + user.setPhone("1234567890"); + user.setOrganisation("org"); + + var appAccess = new AppAccess(); + appAccess.setId(audit.getCreatedBy()); + appAccess.setUser(user); + + when(appAccessRepository.findById(audit.getCreatedBy())) + .thenReturn(Optional.of(appAccess)); + + var results = auditService.findAll( + after, + before, + functionalArea, + source, + userName, + courtId, + caseReference, + pageable + ); + + assertThat(results).isNotNull(); + assertThat(results.getContent()).hasSize(1); + var userDto = results.getContent().getFirst().getCreatedBy(); + assertThat(userDto).isNotNull(); + assertThat(userDto.getId()).isEqualTo(user.getId()); + assertThat(userDto.getFirstName()).isEqualTo(user.getFirstName()); + + verify(appAccessRepository, times(1)).findById(audit.getCreatedBy()); + verify(portalAccessRepository, never()).findById(audit.getCreatedBy()); + } + + @Test + @DisplayName("Search audits and return a list of models with created by from portal access") + void findAllAuditsFindUserPortalAccess() { + when(auditRepository.searchAll( + after, + before, + functionalArea, + source, + userName, + courtId, + caseReference, + pageable + )).thenReturn(new PageImpl<>(List.of(audit))); + + var user = new User(); + user.setId(UUID.randomUUID()); + user.setFirstName("test"); + user.setLastName("user"); + user.setEmail("example@example.com"); + user.setPhone("1234567890"); + user.setOrganisation("org"); + + var portalAccess = new PortalAccess(); + portalAccess.setId(audit.getCreatedBy()); + portalAccess.setUser(user); + + when(appAccessRepository.findById(audit.getCreatedBy())) + .thenReturn(Optional.empty()); + + when(portalAccessRepository.findById(audit.getCreatedBy())) + .thenReturn(Optional.of(portalAccess)); + + var results = auditService.findAll( + after, + before, + functionalArea, + source, + userName, + courtId, + caseReference, + pageable + ); + + assertThat(results).isNotNull(); + assertThat(results.getContent()).hasSize(1); + var userDto = results.getContent().getFirst().getCreatedBy(); + assertThat(userDto).isNotNull(); + assertThat(userDto.getId()).isEqualTo(user.getId()); + assertThat(userDto.getFirstName()).isEqualTo(user.getFirstName()); + + verify(appAccessRepository, times(1)).findById(audit.getCreatedBy()); + verify(portalAccessRepository, times(1)).findById(audit.getCreatedBy()); + } + + @Test + @DisplayName("Search audits and return a list of models with created by not found") + void findAllAuditsFindUserNotFound() { + when(auditRepository.searchAll( + after, + before, + functionalArea, + source, + userName, + courtId, + caseReference, + pageable + )) + .thenReturn(new PageImpl<>(List.of(audit))); + + when(appAccessRepository.findById(audit.getCreatedBy())) + .thenReturn(Optional.empty()); + + when(portalAccessRepository.findById(audit.getCreatedBy())) + .thenReturn(Optional.empty()); + + var results = auditService.findAll( + after, + before, + functionalArea, + source, + userName, + courtId, + caseReference, + pageable + ); + + assertThat(results).isNotNull(); + assertThat(results.getContent()).hasSize(1); + var userDto = results.getContent().getFirst().getCreatedBy(); + assertThat(userDto).isNotNull(); + assertThat(userDto.getId()).isEqualTo(audit.getCreatedBy()); + assertThat(userDto.getFirstName()).isNull(); + + verify(appAccessRepository, times(1)).findById(audit.getCreatedBy()); + verify(portalAccessRepository, times(1)).findById(audit.getCreatedBy()); + } + + @Test + @DisplayName("Should return audit by id") + public void getAuditByIdSuccess() { + audit.setId(UUID.randomUUID()); + when(auditRepository.findById(audit.getId())).thenReturn(Optional.of(audit)); + + var result = auditService.findById(audit.getId()); + + assertThat(result).isNotNull(); + assertThat(result.getId()).isEqualTo(audit.getId()); + assertThat(result.getActivity()).isEqualTo(audit.getActivity()); + assertThat(result.getCategory()).isEqualTo(audit.getCategory()); + assertThat(result.getFunctionalArea()).isEqualTo(audit.getFunctionalArea()); + assertThat(result.getTableName()).isEqualTo(audit.getTableName()); + assertThat(result.getTableRecordId()).isEqualTo(audit.getTableRecordId()); + assertThat(result.getSource()).isEqualTo(audit.getSource()); + assertThat(result.getCreatedBy().getId()).isEqualTo(audit.getCreatedBy()); + } + + @Test + @DisplayName("Should throw not found error when audit by id does not exist") + public void getAuditByIdNotFound() { + var id = UUID.randomUUID(); + when(auditRepository.findById(id)).thenReturn(Optional.empty()); + + var message = assertThrows(NotFoundException.class, () -> auditService.findById(id)) + .getMessage(); + assertThat(message).isEqualTo("Not found: Audit: " + id); + } }