Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package uk.gov.hmcts.sptribs.controllers;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import uk.gov.hmcts.reform.authorisation.generators.AuthTokenGenerator;
import uk.gov.hmcts.sptribs.cdam.model.Document;
import uk.gov.hmcts.sptribs.common.config.WebMvcConfig;
import uk.gov.hmcts.sptribs.services.cdam.CaseDocumentClientApi;
import uk.gov.hmcts.sptribs.testutil.IdamWireMock;

import java.util.UUID;

import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static uk.gov.hmcts.sptribs.testutil.TestConstants.AUTHORIZATION;
import static uk.gov.hmcts.sptribs.testutil.TestConstants.SERVICE_AUTHORIZATION;
import static uk.gov.hmcts.sptribs.testutil.TestConstants.TEST_AUTHORIZATION_TOKEN;
import static uk.gov.hmcts.sptribs.testutil.TestConstants.TEST_SERVICE_AUTH_TOKEN;

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
@ContextConfiguration(initializers = {IdamWireMock.PropertiesInitializer.class})
class DocumentControllerIT {

private static final String DOWNLOAD_DOCUMENT_URL = "/case/document/downloadDocument/";

@Autowired
private MockMvc mockMvc;

@MockitoBean
private WebMvcConfig webMvcConfig;

@MockitoBean
private AuthTokenGenerator authTokenGenerator;

@MockitoBean
private CaseDocumentClientApi caseDocumentClientApi;

@BeforeAll
static void setUp() {
IdamWireMock.start();
}

@AfterAll
static void tearDown() {
IdamWireMock.stopAndReset();
}

@Test
void shouldDownloadDocumentSuccessfully() throws Exception {
// Given
String fileName = "test-document.pdf";
String mimeType = "application/pdf";

Document document = new Document();
document.originalDocumentName = fileName;
document.mimeType = mimeType;
UUID documentId = UUID.randomUUID();

when(authTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN);
when(caseDocumentClientApi.getDocument(
eq(TEST_AUTHORIZATION_TOKEN),
eq(TEST_SERVICE_AUTH_TOKEN),
eq(documentId)
)).thenReturn(ResponseEntity.ok(document));

byte[] documentContent = "test document content".getBytes();
when(caseDocumentClientApi.getDocumentBinary(
eq(TEST_AUTHORIZATION_TOKEN),
eq(TEST_SERVICE_AUTH_TOKEN),
eq(documentId)
)).thenReturn(ResponseEntity.ok(documentContent));

// When & Then
mockMvc.perform(get(DOWNLOAD_DOCUMENT_URL + documentId)
.header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN)
.header(SERVICE_AUTHORIZATION, TEST_SERVICE_AUTH_TOKEN))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, mimeType))
.andExpect(header().string("original-file-name", fileName))
.andExpect(content().bytes(documentContent));
}

@Test
void shouldReturn500WhenDocumentNotFound() throws Exception {
// Given
UUID documentId = UUID.randomUUID();

when(authTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN);
when(caseDocumentClientApi.getDocument(
eq(TEST_AUTHORIZATION_TOKEN),
eq(TEST_SERVICE_AUTH_TOKEN),
eq(documentId)
)).thenReturn(ResponseEntity.ok(null));

// When & Then
mockMvc.perform(get(DOWNLOAD_DOCUMENT_URL + documentId)
.header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN)
.header(SERVICE_AUTHORIZATION, TEST_SERVICE_AUTH_TOKEN))
.andExpect(status().isInternalServerError());
}

@Test
void shouldReturn500WhenDocumentBinaryNotFound() throws Exception {
// Given
String fileName = "test-document.pdf";
String mimeType = "application/pdf";

Document document = new Document();
document.originalDocumentName = fileName;
document.mimeType = mimeType;
UUID documentId = UUID.randomUUID();

when(authTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN);
when(caseDocumentClientApi.getDocument(
eq(TEST_AUTHORIZATION_TOKEN),
eq(TEST_SERVICE_AUTH_TOKEN),
eq(documentId)
)).thenReturn(ResponseEntity.ok(document));
when(caseDocumentClientApi.getDocumentBinary(
eq(TEST_AUTHORIZATION_TOKEN),
eq(TEST_SERVICE_AUTH_TOKEN),
eq(documentId)
)).thenReturn(ResponseEntity.ok(null));

// When & Then
mockMvc.perform(get(DOWNLOAD_DOCUMENT_URL + documentId)
.header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN)
.header(SERVICE_AUTHORIZATION, TEST_SERVICE_AUTH_TOKEN))
.andExpect(status().isInternalServerError());
}

@Test
void shouldReturn500WhenApiCallFails() throws Exception {
// Given
UUID documentId = UUID.randomUUID();

when(authTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN);
when(caseDocumentClientApi.getDocument(
eq(TEST_AUTHORIZATION_TOKEN),
eq(TEST_SERVICE_AUTH_TOKEN),
eq(documentId)
)).thenThrow(new RuntimeException("API error"));

// When & Then
mockMvc.perform(get(DOWNLOAD_DOCUMENT_URL + documentId)
.header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN)
.header(SERVICE_AUTHORIZATION, TEST_SERVICE_AUTH_TOKEN))
.andExpect(status().isInternalServerError());
}

@Test
void shouldReturn500ForInvalidDocumentId() throws Exception {
// Given
String invalidDocumentId = "invalid-uuid-format";

// When & Then
mockMvc.perform(get(DOWNLOAD_DOCUMENT_URL + invalidDocumentId)
.header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN)
.header(SERVICE_AUTHORIZATION, TEST_SERVICE_AUTH_TOKEN))
.andExpect(status().isInternalServerError());
}

@Test
void shouldDownloadDocumentWithDifferentMimeTypes() throws Exception {
// Given
String fileName = "test-document.html";
String mimeType = "text/html";

Document document = new Document();
document.originalDocumentName = fileName;
document.mimeType = mimeType;

UUID documentId = UUID.randomUUID();

when(authTokenGenerator.generate()).thenReturn(TEST_SERVICE_AUTH_TOKEN);
when(caseDocumentClientApi.getDocument(
eq(TEST_AUTHORIZATION_TOKEN),
eq(TEST_SERVICE_AUTH_TOKEN),
eq(documentId)
)).thenReturn(ResponseEntity.ok(document));

byte[] documentContent = "<html><body>Test</body></html>".getBytes();
when(caseDocumentClientApi.getDocumentBinary(
eq(TEST_AUTHORIZATION_TOKEN),
eq(TEST_SERVICE_AUTH_TOKEN),
eq(documentId)
)).thenReturn(ResponseEntity.ok(documentContent));

// When & Then
mockMvc.perform(get(DOWNLOAD_DOCUMENT_URL + documentId)
.header(AUTHORIZATION, TEST_AUTHORIZATION_TOKEN)
.header(SERVICE_AUTHORIZATION, TEST_SERVICE_AUTH_TOKEN))
.andExpect(status().isOk())
.andExpect(header().string(HttpHeaders.CONTENT_TYPE, mimeType))
.andExpect(header().string("original-file-name", fileName))
.andExpect(content().bytes(documentContent));
}
}







2 changes: 2 additions & 0 deletions src/main/java/uk/gov/hmcts/sptribs/ciccase/CicCaseView.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public CriminalInjuriesCompensationData getCase(CaseViewRequest<State> request,
correspondences.add(correspondenceListValue);
}

blobCase.getCicCase().setApplicantDocuments(blobCase.getCicCase().getApplicantDocumentsUploaded());

blobCase.setCorrespondence(correspondences);
return blobCase;
}
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/uk/gov/hmcts/sptribs/ciccase/model/CicCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import uk.gov.hmcts.sptribs.ciccase.model.access.CaseworkerAccess;
import uk.gov.hmcts.sptribs.ciccase.model.access.CaseworkerAndSuperUserAccess;
import uk.gov.hmcts.sptribs.ciccase.model.access.CaseworkerWithCAAAccess;
import uk.gov.hmcts.sptribs.ciccase.model.access.CitizenAccess;
import uk.gov.hmcts.sptribs.ciccase.model.access.CollectionDefaultAccess;
import uk.gov.hmcts.sptribs.ciccase.model.access.DefaultAccess;
import uk.gov.hmcts.sptribs.document.model.CICDocument;
Expand Down Expand Up @@ -461,6 +462,12 @@ public class CicCase {
)
private String applicantFullName;

@CCD(
label = "Applicant's full name",
access = {DefaultAccess.class, CaseworkerWithCAAAccess.class, CitizenAccess.class}
)
private List<ListValue<CaseworkerCICDocument>> applicantDocuments;

@CCD(
label = "Applicant's address",
access = {DefaultAccess.class, CaseworkerWithCAAAccess.class}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package uk.gov.hmcts.sptribs.ciccase.repository;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import uk.gov.hmcts.sptribs.controllers.model.CicaCaseResponse;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Slf4j
@Repository
@RequiredArgsConstructor
public class CicaCaseRepository {

private static final TypeReference<Map<String, JsonNode>> JSON_NODE_MAP = new TypeReference<>() { };

private static final String CASE_TYPE = "CriminalInjuriesCompensation";
private static final String JURISDICTION = "ST_CIC";
private static final String CICA_REFERENCE_JSON_PATH = "{editCicaCaseDetails,cicaReferenceNumber}";

private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
private final ObjectMapper objectMapper;

/**
* Finds a case by CICA reference number.
* Searches in the data JSONB column for the editCicaCaseDetails.cicaReferenceNumber field.
* Returns the most recently modified case if multiple matches are found.
*
* @param cicaReference the CICA reference number to search for (case-insensitive)
* @return Optional containing the case if found, empty otherwise
*/
public Optional<CicaCaseResponse> findByCicaReference(String cicaReference) {
log.info("Searching for case with CICA reference: {}", cicaReference);

var params = Map.of(
"cicaReference", cicaReference.toUpperCase(),
"caseType", CASE_TYPE,
"jurisdiction", JURISDICTION
);

String sql = """
SELECT
c.id,
c.reference,
c.state,
c.data::text AS case_data,
c.last_modified
FROM ccd.case_data c
WHERE c.case_type_id = :caseType
AND c.jurisdiction = :jurisdiction
AND UPPER(c.data #>> '%s') = :cicaReference
ORDER BY c.last_modified DESC
LIMIT 1
""".formatted(CICA_REFERENCE_JSON_PATH);

List<CicaCaseResponse> results = namedParameterJdbcTemplate.query(
sql,
params,
(rs, rowNum) -> mapToCicaCaseResponse(rs)
);

if (results.isEmpty()) {
log.info("No case found with CICA reference: {}", cicaReference);
return Optional.empty();
}

log.info("Found case with CICA reference: {}", cicaReference);
return Optional.of(results.get(0));
}

@SneakyThrows
private CicaCaseResponse mapToCicaCaseResponse(ResultSet rs) throws SQLException {
Long reference = rs.getObject("reference", Long.class);
String state = rs.getString("state");
String caseDataJson = rs.getString("case_data");

Map<String, JsonNode> caseData = objectMapper.readValue(caseDataJson, JSON_NODE_MAP);

return CicaCaseResponse.builder()
.id(String.valueOf(reference))
.state(state)
.data(caseData)
.build();
}
}



Loading
Loading