diff --git a/build.gradle b/build.gradle index 4d0271b8b7c..e15a3a75dd5 100644 --- a/build.gradle +++ b/build.gradle @@ -707,4 +707,3 @@ bootWithCCD { test { useJUnitPlatform() } - diff --git a/charts/prl-cos/values.preview.template.yaml b/charts/prl-cos/values.preview.template.yaml index 796b35d8495..c3654e15221 100644 --- a/charts/prl-cos/values.preview.template.yaml +++ b/charts/prl-cos/values.preview.template.yaml @@ -105,5 +105,6 @@ java: COMMON_DATA_API: http://rd-commondata-api-aat.service.core-compute-aat.internal SEND_LETTER_URL: http://rpe-send-letter-service-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal BARRISTER_FEATURE_ENABLED: true + AWAITING_INFORMATION_ENABLED: true CAFCASS_DATE_TIME_FEATURE_ENABLED: true APP_ENV: "preview" diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/prl/controllers/RequestFurtherInformationControllerIntegrationTest.java b/src/integrationTest/java/uk/gov/hmcts/reform/prl/controllers/RequestFurtherInformationControllerIntegrationTest.java new file mode 100644 index 00000000000..2cb08ee246a --- /dev/null +++ b/src/integrationTest/java/uk/gov/hmcts/reform/prl/controllers/RequestFurtherInformationControllerIntegrationTest.java @@ -0,0 +1,400 @@ +package uk.gov.hmcts.reform.prl.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.module.paramnames.ParameterNamesModule; +import lombok.extern.slf4j.Slf4j; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +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.http.MediaType; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.context.WebApplicationContext; +import uk.gov.hmcts.reform.prl.ResourceLoader; +import uk.gov.hmcts.reform.prl.models.complextypes.tab.summarytab.summary.CaseStatus; +import uk.gov.hmcts.reform.prl.services.AuthorisationService; +import uk.gov.hmcts.reform.prl.services.FeatureToggleService; +import uk.gov.hmcts.reform.prl.services.RequestFurtherInformationService; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; +import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.CASE_STATUS; + +@Slf4j +@SpringBootTest(properties = { + "feature.toggle.awaitingInformationEnabled=true" +}) +@RunWith(SpringRunner.class) +@ContextConfiguration +public class RequestFurtherInformationControllerIntegrationTest { + + private MockMvc mockMvc; + + @Autowired + private WebApplicationContext webApplicationContext; + + @MockBean + private AuthorisationService authorisationService; + + @MockBean + private RequestFurtherInformationService requestFurtherInformationService; + + @MockBean + private FeatureToggleService featureToggleService; + + @Autowired + private ObjectMapper objectMapper; + + private static final String AUTHORISATION_HEADER = "Authorization"; + private static final String SERVICE_AUTHORISATION_HEADER = "Service-Authorization"; + private static final String TEST_AUTH_TOKEN = "Bearer testAuthToken"; + private static final String TEST_SERVICE_AUTH_TOKEN = "testServiceAuthToken"; + + @Before + public void setUp() { + this.mockMvc = webAppContextSetup(webApplicationContext).build(); + objectMapper.registerModule(new ParameterNamesModule()); + when(featureToggleService.isAwaitingInformationEnabled()).thenReturn(true); + } + + private Map createMockCaseData() { + Map caseData = new HashMap<>(); + caseData.put("id", 12345678L); + caseData.put(CASE_STATUS, CaseStatus.builder().state("Awaiting information").build()); + return caseData; + } + + // Tests for /submit-request-further-information endpoint + + @Test + public void shouldSubmitAwaitingInformationSuccessfully() throws Exception { + String url = "/submit-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + when(authorisationService.isAuthorized(any(), any())).thenReturn(true); + when(requestFurtherInformationService.addToCase(any())).thenReturn(createMockCaseData()); + + mockMvc.perform( + post(url) + .header(AUTHORISATION_HEADER, TEST_AUTH_TOKEN) + .header(SERVICE_AUTHORISATION_HEADER, TEST_SERVICE_AUTH_TOKEN) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").exists()) + .andReturn(); + } + + @Test + public void shouldRejectSubmitAwaitingInformationWithoutAuthorizationHeader() throws Exception { + String url = "/submit-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + mockMvc.perform( + post(url) + .header(SERVICE_AUTHORISATION_HEADER, TEST_SERVICE_AUTH_TOKEN) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isBadRequest()) + .andReturn(); + } + + @Test + public void shouldRejectSubmitAwaitingInformationWithoutServiceAuthorizationHeader() throws Exception { + String url = "/submit-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + mockMvc.perform( + post(url) + .header(AUTHORISATION_HEADER, TEST_AUTH_TOKEN) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isBadRequest()) + .andReturn(); + } + + @Test + public void shouldRejectSubmitAwaitingInformationWithUnauthorizedTokens() throws Exception { + String url = "/submit-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + when(authorisationService.isAuthorized(any(), any())).thenReturn(false); + + mockMvc.perform( + post(url) + .header(AUTHORISATION_HEADER, "invalidToken") + .header(SERVICE_AUTHORISATION_HEADER, "invalidServiceToken") + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isInternalServerError()) + .andReturn(); + } + + @Test + public void shouldSubmitAwaitingInformationWithValidHeaders() throws Exception { + String url = "/submit-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + when(authorisationService.isAuthorized(TEST_AUTH_TOKEN, TEST_SERVICE_AUTH_TOKEN)).thenReturn(true); + when(requestFurtherInformationService.addToCase(any())).thenReturn(createMockCaseData()); + + mockMvc.perform( + post(url) + .header(AUTHORISATION_HEADER, TEST_AUTH_TOKEN) + .header(SERVICE_AUTHORISATION_HEADER, TEST_SERVICE_AUTH_TOKEN) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.id").value(12345678)) + .andReturn(); + } + + @Test + public void shouldHandleSubmitAwaitingInformationWithAdditionalCaseData() throws Exception { + Map caseData = createMockCaseData(); + caseData.put("applicantName", "John Doe"); + caseData.put("respondentName", "Jane Doe"); + + when(authorisationService.isAuthorized(any(), any())).thenReturn(true); + when(requestFurtherInformationService.addToCase(any())).thenReturn(caseData); + + String url = "/submit-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + mockMvc.perform( + post(url) + .header(AUTHORISATION_HEADER, TEST_AUTH_TOKEN) + .header(SERVICE_AUTHORISATION_HEADER, TEST_SERVICE_AUTH_TOKEN) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.applicantName").value("John Doe")) + .andExpect(jsonPath("$.data.respondentName").value("Jane Doe")) + .andReturn(); + } + + // Tests for /validate-request-further-information endpoint + + @Test + public void shouldValidateAwaitingInformationSuccessfully() throws Exception { + String url = "/validate-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + when(requestFurtherInformationService.validate(any(uk.gov.hmcts.reform.ccd.client.model.CallbackRequest.class))) + .thenReturn(new ArrayList<>()); + + mockMvc.perform( + post(url) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").isArray()) + .andReturn(); + } + + @Test + public void shouldValidateAwaitingInformationWithErrors() throws Exception { + String url = "/validate-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + List errorList = new ArrayList<>(); + errorList.add("Please enter a future date"); + + when(requestFurtherInformationService.validate(any(uk.gov.hmcts.reform.ccd.client.model.CallbackRequest.class))) + .thenReturn(errorList); + + mockMvc.perform( + post(url) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.errors[0]").value("Please enter a future date")) + .andReturn(); + } + + @Test + public void shouldValidateAwaitingInformationWithMultipleErrors() throws Exception { + + List errorList = new ArrayList<>(); + errorList.add("Please enter a future date"); + errorList.add("Review date cannot be more than 12 months away"); + + when(requestFurtherInformationService.validate(any(uk.gov.hmcts.reform.ccd.client.model.CallbackRequest.class))) + .thenReturn(errorList); + + String url = "/validate-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + mockMvc.perform( + post(url) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.errors.length()").value(2)) + .andReturn(); + } + + @Test + public void shouldValidateAwaitingInformationReturnEmptyErrorsWhenFeatureToggleDisabled() throws Exception { + String url = "/validate-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + when(featureToggleService.isAwaitingInformationEnabled()).thenReturn(false); + + mockMvc.perform( + post(url) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.errors").isEmpty()) + .andReturn(); + } + + @Test + public void shouldValidateAwaitingInformationWithCorrectContentType() throws Exception { + String url = "/validate-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + when(requestFurtherInformationService.validate(any(uk.gov.hmcts.reform.ccd.client.model.CallbackRequest.class))) + .thenReturn(new ArrayList<>()); + + mockMvc.perform( + post(url) + .accept(MediaType.APPLICATION_JSON) + .contentType(MediaType.APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andReturn(); + } + + @Test + public void shouldHandleValidateAwaitingInformationWithoutContentType() throws Exception { + String url = "/validate-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + when(requestFurtherInformationService.validate(any(uk.gov.hmcts.reform.ccd.client.model.CallbackRequest.class))) + .thenReturn(new ArrayList<>()); + + mockMvc.perform( + post(url) + .content(jsonRequest)) + .andExpect(status().isUnsupportedMediaType()) + .andReturn(); + } + + // Integration workflow tests + + @Test + public void shouldHandleCompleteAwaitingInformationWorkflow() throws Exception { + String submitUrl = "/submit-request-further-information"; + String validateUrl = "/validate-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + when(authorisationService.isAuthorized(any(), any())).thenReturn(true); + when(requestFurtherInformationService.addToCase(any())).thenReturn(createMockCaseData()); + when(requestFurtherInformationService.validate(any(uk.gov.hmcts.reform.ccd.client.model.CallbackRequest.class))) + .thenReturn(new ArrayList<>()); + + // Submit awaiting information + mockMvc.perform( + post(submitUrl) + .header(AUTHORISATION_HEADER, TEST_AUTH_TOKEN) + .header(SERVICE_AUTHORISATION_HEADER, TEST_SERVICE_AUTH_TOKEN) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andReturn(); + + // Validate awaiting information + mockMvc.perform( + post(validateUrl) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andReturn(); + } + + @Test + public void shouldHandleSequentialValidationCalls() throws Exception { + String url = "/validate-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + when(requestFurtherInformationService.validate(any(uk.gov.hmcts.reform.ccd.client.model.CallbackRequest.class))) + .thenReturn(new ArrayList<>()); + + // First validation call + mockMvc.perform( + post(url) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andReturn(); + + // Second validation call + mockMvc.perform( + post(url) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andReturn(); + } + + @Test + public void shouldHandleMultipleSubmitCalls() throws Exception { + String url = "/submit-request-further-information"; + String jsonRequest = ResourceLoader.loadJson("CallbackRequest.json"); + + when(authorisationService.isAuthorized(any(), any())).thenReturn(true); + when(requestFurtherInformationService.addToCase(any())).thenReturn(createMockCaseData()); + + // First submit + mockMvc.perform( + post(url) + .header(AUTHORISATION_HEADER, TEST_AUTH_TOKEN) + .header(SERVICE_AUTHORISATION_HEADER, TEST_SERVICE_AUTH_TOKEN) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andReturn(); + + // Second submit + mockMvc.perform( + post(url) + .header(AUTHORISATION_HEADER, TEST_AUTH_TOKEN) + .header(SERVICE_AUTHORISATION_HEADER, TEST_SERVICE_AUTH_TOKEN) + .accept(APPLICATION_JSON) + .contentType(APPLICATION_JSON) + .content(jsonRequest)) + .andExpect(status().isOk()) + .andReturn(); + } +} + diff --git a/src/main/java/uk/gov/hmcts/reform/prl/constants/PrlAppsConstants.java b/src/main/java/uk/gov/hmcts/reform/prl/constants/PrlAppsConstants.java index db547310b7b..1c066989f0c 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/constants/PrlAppsConstants.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/constants/PrlAppsConstants.java @@ -1126,6 +1126,8 @@ public class PrlAppsConstants { public static final String CASE_STATUS = "caseStatus"; + public static final String REQUEST_FURTHER_INFORMATION_DETAILS = "requestFurtherInformationDetails"; + public static final String FETCH_FEE_INVALID_APPLICATION_TYPE = "Invalid application type to fetch fee details: "; public static final String FETCH_FEE_ERROR = "Error while fetching fee details for application type: "; public static final String ENGLISH = "en"; diff --git a/src/main/java/uk/gov/hmcts/reform/prl/controllers/CallbackController.java b/src/main/java/uk/gov/hmcts/reform/prl/controllers/CallbackController.java index 96f23be7ab2..b4c95f7b14c 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/controllers/CallbackController.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/controllers/CallbackController.java @@ -125,6 +125,8 @@ import static uk.gov.hmcts.reform.prl.constants.PrlLaunchDarklyFlagConstants.TASK_LIST_V2_FLAG; import static uk.gov.hmcts.reform.prl.constants.PrlLaunchDarklyFlagConstants.TASK_LIST_V3_FLAG; import static uk.gov.hmcts.reform.prl.enums.Event.SEND_TO_GATEKEEPER; +import static uk.gov.hmcts.reform.prl.enums.State.AWAITING_INFORMATION; +import static uk.gov.hmcts.reform.prl.enums.State.CASE_ISSUED; import static uk.gov.hmcts.reform.prl.enums.State.SUBMITTED_PAID; import static uk.gov.hmcts.reform.prl.enums.YesOrNo.Yes; import static uk.gov.hmcts.reform.prl.utils.CaseUtils.getCaseData; @@ -696,15 +698,25 @@ public AboutToStartOrSubmitCallbackResponse addCaseNumberSubmitted( ) { if (authorisationService.isAuthorized(authorisation, s2sToken)) { Map caseDataUpdated = callbackRequest.getCaseDetails().getData(); + CaseData caseData = getCaseData(callbackRequest.getCaseDetails(), objectMapper); List eventsForCase = caseEventService.findEventsForCase(String.valueOf(callbackRequest.getCaseDetails().getId())); - Optional previousState = eventsForCase.stream() + Optional previousStateOpt = eventsForCase.stream() .map(CaseEventDetail::getStateId) .findFirst(); - previousState.ifPresent(s -> caseDataUpdated.put( - VERIFY_CASE_NUMBER_ADDED, - SUBMITTED_PAID.getValue().equalsIgnoreCase(s) ? Yes.getDisplayedValue() : null - )); + + if (previousStateOpt.isPresent()) { + String previousState = previousStateOpt.get(); + if (AWAITING_INFORMATION.getValue().equalsIgnoreCase(previousState)) { + caseDataUpdated.put(VERIFY_CASE_NUMBER_ADDED, Yes.getDisplayedValue()); + caseData = caseData.toBuilder().state(CASE_ISSUED).build(); + caseDataUpdated.putAll(caseSummaryTab.updateTab(caseData)); + caseDataUpdated.put(STATE_FIELD, CASE_ISSUED.getValue()); + } else if (SUBMITTED_PAID.getValue().equalsIgnoreCase(previousState)) { + caseDataUpdated.put(VERIFY_CASE_NUMBER_ADDED, Yes.getDisplayedValue()); + } + } + caseDataUpdated.put(ISSUE_DATE_FIELD, LocalDate.now()); return AboutToStartOrSubmitCallbackResponse.builder() .data(caseDataUpdated) @@ -940,4 +952,3 @@ public AboutToStartOrSubmitCallbackResponse updateOtherPeoplePartyDetails( } } } - diff --git a/src/main/java/uk/gov/hmcts/reform/prl/controllers/RequestFurtherInformationController.java b/src/main/java/uk/gov/hmcts/reform/prl/controllers/RequestFurtherInformationController.java new file mode 100644 index 00000000000..bff9d9abfb6 --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/prl/controllers/RequestFurtherInformationController.java @@ -0,0 +1,99 @@ +package uk.gov.hmcts.reform.prl.controllers; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RestController; +import uk.gov.hmcts.reform.ccd.client.model.AboutToStartOrSubmitCallbackResponse; +import uk.gov.hmcts.reform.ccd.client.model.CallbackRequest; +import uk.gov.hmcts.reform.ccd.client.model.SubmittedCallbackResponse; +import uk.gov.hmcts.reform.prl.constants.PrlAppsConstants; +import uk.gov.hmcts.reform.prl.models.dto.ccd.CallbackResponse; +import uk.gov.hmcts.reform.prl.services.AuthorisationService; +import uk.gov.hmcts.reform.prl.services.FeatureToggleService; +import uk.gov.hmcts.reform.prl.services.RequestFurtherInformationService; + +import static java.util.Collections.emptyList; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON; +import static org.springframework.http.ResponseEntity.ok; +import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.INVALID_CLIENT; + +@Slf4j +@RestController +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RequestFurtherInformationController { + private final RequestFurtherInformationService requestFurtherInformationService; + private final AuthorisationService authorisationService; + private final FeatureToggleService featureToggleService; + public static final String CONFIRMATION_HEADER = "confirmationHeader"; + + @PostMapping(path = "/submit-request-further-information", consumes = APPLICATION_JSON, produces = APPLICATION_JSON) + @Operation(description = "Awaiting On Information callback to update case data and set case status to awaiting information") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Callback processed.", + content = @Content(mediaType = "application/json", schema = @Schema(implementation = AboutToStartOrSubmitCallbackResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content)}) + @SecurityRequirement(name = "Bearer Authentication") + public AboutToStartOrSubmitCallbackResponse submitAwaitingInformation( + @RequestHeader(HttpHeaders.AUTHORIZATION) @Parameter(hidden = true) String authorisation, + @RequestHeader(PrlAppsConstants.SERVICE_AUTHORIZATION_HEADER) String s2sToken, + @RequestBody CallbackRequest callbackRequest + ) { + if (authorisationService.isAuthorized(authorisation, s2sToken) + && featureToggleService.isAwaitingInformationEnabled()) { + var caseDataUpdated = requestFurtherInformationService.addToCase(callbackRequest); + return AboutToStartOrSubmitCallbackResponse.builder().data(caseDataUpdated).build(); + } + throw (new RuntimeException(INVALID_CLIENT)); + } + + + @PostMapping(path = "/validate-request-further-information", consumes = APPLICATION_JSON, produces = APPLICATION_JSON) + @Operation(description = "Callback to validate review date") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Child details are fetched"), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content)}) + public CallbackResponse validateReviewDate(@RequestBody CallbackRequest callbackRequest) { + if (!featureToggleService.isAwaitingInformationEnabled()) { + return CallbackResponse.builder() + .errors(emptyList()) + .build(); + } + CallbackResponse build = CallbackResponse.builder() + .errors(requestFurtherInformationService.validate(callbackRequest)) + .build(); + return build; + } + + @PostMapping(path = "/history-update", consumes = APPLICATION_JSON, produces = APPLICATION_JSON) + @Operation(description = "Callback to update History Tab with Awaiting Information . Returns service request reference if " + + "successful") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Callback processed.", + content = @Content(mediaType = "application/json", + schema = @Schema(implementation = uk.gov.hmcts.reform.ccd.client.model.CallbackResponse.class))), + @ApiResponse(responseCode = "400", description = "Bad Request", content = @Content)}) + public ResponseEntity historyUpdated( + @RequestHeader(HttpHeaders.AUTHORIZATION) @Parameter(hidden = true) String authorisation, + @RequestHeader(PrlAppsConstants.SERVICE_AUTHORIZATION_HEADER) String s2sToken, + @RequestBody CallbackRequest callbackRequest) { + if (authorisationService.isAuthorized(authorisation, s2sToken) + && featureToggleService.isAwaitingInformationEnabled()) { + requestFurtherInformationService.updateHistoryTab(callbackRequest); + return ok(SubmittedCallbackResponse.builder().build()); + } + throw (new RuntimeException(INVALID_CLIENT)); + } +} diff --git a/src/main/java/uk/gov/hmcts/reform/prl/enums/CaseEvent.java b/src/main/java/uk/gov/hmcts/reform/prl/enums/CaseEvent.java index e940e4f01d0..b88af474f34 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/enums/CaseEvent.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/enums/CaseEvent.java @@ -76,7 +76,9 @@ public enum CaseEvent { AMEND_OTHER_PEOPLE_IN_THE_CASE_REVISED("amendOtherPeopleInTheCaseRevised"), APPLICANT_DETAILS("applicantsDetails"), REVIEW_ADDITIONAL_APPLICATION("reviewAdditionalApplication"), - CLOSE_REVIEW_RA_REQUEST_TASK("closeReviewRARequestTask"); + CLOSE_REVIEW_RA_REQUEST_TASK("closeReviewRARequestTask"), + REQUEST_FURTHER_INFORMATION("requestFurtherInformation"), + REQUEST_FURTHER_INFORMATION_HISTORY("requestFurtherInformationHistory"); private final String value; diff --git a/src/main/java/uk/gov/hmcts/reform/prl/enums/State.java b/src/main/java/uk/gov/hmcts/reform/prl/enums/State.java index 638b493c461..df49d4322c8 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/enums/State.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/enums/State.java @@ -30,7 +30,9 @@ public enum State { READY_FOR_DELETION("READY_FOR_DELETION", "Ready for deletion"), DECISION_OUTCOME("DECISION_OUTCOME","Hearing Outcome"), PROCEEDS_IN_HERITAGE_SYSTEM("PROCEEDS_IN_HERITAGE_SYSTEM", - "Proceeding in offline mode in familyman system"); + "Proceeding in offline mode in familyman system"), + AWAITING_INFORMATION("AWAITING_INFORMATION", + "Awaiting information"); private final String value; private final String label; diff --git a/src/main/java/uk/gov/hmcts/reform/prl/enums/awaitinginformation/RequestFurtherInformationReasonEnum.java b/src/main/java/uk/gov/hmcts/reform/prl/enums/awaitinginformation/RequestFurtherInformationReasonEnum.java new file mode 100644 index 00000000000..07c763fff2d --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/prl/enums/awaitinginformation/RequestFurtherInformationReasonEnum.java @@ -0,0 +1,44 @@ +package uk.gov.hmcts.reform.prl.enums.awaitinginformation; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import lombok.RequiredArgsConstructor; +import uk.gov.hmcts.reform.prl.enums.CustomEnumSerializer; + +@RequiredArgsConstructor +@JsonSerialize(using = CustomEnumSerializer.class) +public enum RequestFurtherInformationReasonEnum { + + @JsonProperty("miamFurtherInformation") + miamFurtherInformation("miamFurtherInformation", "MIAM - further information required"), + @JsonProperty("dwpHmrcWhereaboutsUnknown") + dwpHmrcWhereaboutsUnknown("dwpHmrcWhereaboutsUnknown", "DWP/HMRC - whereabouts unknown"), + @JsonProperty("applicantFurtherInformation") + applicantFurtherInformation("applicantFurtherInformation", "Applicant - further information required"), + @JsonProperty("applicantClarifyConfidentialDetails") + applicantClarifyConfidentialDetails("applicantClarifyConfidentialDetails", "Applicant - clarify confidential details"), + @JsonProperty("respondentFurtherInformation") + respondentFurtherInformation("respondentFurtherInformation", "Respondent - further information required"), + @JsonProperty("helpWithFeesFurtherAction") + helpWithFeesFurtherAction("helpWithFeesFurtherAction", "Help with Fees - further action required"), + @JsonProperty("ctscRefundRequired") + ctscRefundRequired("ctscRefundRequired", "CTSC - Refund required"), + @JsonProperty("other") + other("other", "Another reason that has not been listed"); + + private final String id; + private final String displayedValue; + + @JsonValue + public String getDisplayedValue() { + return displayedValue; + } + + @JsonCreator + public static RequestFurtherInformationReasonEnum getValue(String key) { + return RequestFurtherInformationReasonEnum.valueOf(key); + } + +} diff --git a/src/main/java/uk/gov/hmcts/reform/prl/models/Features.java b/src/main/java/uk/gov/hmcts/reform/prl/models/Features.java index 58c6cccddab..416f14ebb07 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/models/Features.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/models/Features.java @@ -9,7 +9,8 @@ public enum Features { IS_BARRISTER_FEATURE_ENABLED("barristerFeatureEnabled"), IS_CAFCASS_DATE_TIME_FEATURE_ENABLED("cafcassDateTimeFeatureEnabled"), - IS_OS_COURT_LOOKUP_ENABLED("osCourtLookupEnabled"); + IS_OS_COURT_LOOKUP_ENABLED("osCourtLookupEnabled"), + IS_AWAITING_INFORMATION_ENABLED("awaitingInformationEnabled"); private final String name; } diff --git a/src/main/java/uk/gov/hmcts/reform/prl/models/dto/ccd/RequestFurtherInformation.java b/src/main/java/uk/gov/hmcts/reform/prl/models/dto/ccd/RequestFurtherInformation.java new file mode 100644 index 00000000000..924fea0c62b --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/prl/models/dto/ccd/RequestFurtherInformation.java @@ -0,0 +1,22 @@ +package uk.gov.hmcts.reform.prl.models.dto.ccd; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import uk.gov.hmcts.reform.prl.enums.awaitinginformation.RequestFurtherInformationReasonEnum; + +import java.time.LocalDate; +import java.util.List; + +@Data +@Builder(toBuilder = true) +@AllArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class RequestFurtherInformation { + @JsonProperty("reviewByDate") + private LocalDate reviewDate; + @JsonProperty("requestFurtherInformationReasonList") + private List requestFurtherInformationReasonEnum; +} diff --git a/src/main/java/uk/gov/hmcts/reform/prl/services/FeatureToggleService.java b/src/main/java/uk/gov/hmcts/reform/prl/services/FeatureToggleService.java index 00f5d6b1fbb..0aee0f13cc3 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/services/FeatureToggleService.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/services/FeatureToggleService.java @@ -55,4 +55,8 @@ public boolean isCafcassDateTimeFeatureEnabled() { public boolean isOsCourtLookupFeatureEnabled() { return isFeatureEnabled(IS_OS_COURT_LOOKUP_ENABLED); } + + public boolean isAwaitingInformationEnabled() { + return isFeatureEnabled(Features.IS_AWAITING_INFORMATION_ENABLED); + } } diff --git a/src/main/java/uk/gov/hmcts/reform/prl/services/HelpWithFeesService.java b/src/main/java/uk/gov/hmcts/reform/prl/services/HelpWithFeesService.java index 56a68d571de..3f10c9fcab1 100644 --- a/src/main/java/uk/gov/hmcts/reform/prl/services/HelpWithFeesService.java +++ b/src/main/java/uk/gov/hmcts/reform/prl/services/HelpWithFeesService.java @@ -97,7 +97,8 @@ public ResponseEntity handleSubmitted() { public Map setCaseStatus(CallbackRequest callbackRequest, String authorisation) { Map caseDataUpdated = callbackRequest.getCaseDetails().getData(); CaseData caseData = CaseUtils.getCaseData(callbackRequest.getCaseDetails(), objectMapper); - if (callbackRequest.getCaseDetails().getState().equalsIgnoreCase((State.SUBMITTED_NOT_PAID.getValue()))) { + if (callbackRequest.getCaseDetails().getState().equalsIgnoreCase(State.SUBMITTED_NOT_PAID.getValue()) + || callbackRequest.getCaseDetails().getState().equalsIgnoreCase(State.AWAITING_INFORMATION.getValue())) { caseDataUpdated.put(CASE_STATUS, CaseStatus.builder() .state(SUBMITTED_PAID.getLabel()) .build()); @@ -176,7 +177,10 @@ public Map handleAboutToStart(CaseDetails caseDetails) { CaseData caseData = CaseUtils.getCaseData(caseDetails, objectMapper); if (null != caseData) { - if (State.SUBMITTED_NOT_PAID.getValue().equalsIgnoreCase(caseDetails.getState()) && YesOrNo.Yes.equals(caseData.getHelpWithFees())) { + if ((State.SUBMITTED_NOT_PAID.getValue().equalsIgnoreCase(caseDetails.getState()) + || State.AWAITING_INFORMATION.getValue().equalsIgnoreCase(caseDetails.getState())) + && YesOrNo.Yes.equals(caseData.getHelpWithFees())) { + String dynamicElement = String.format("Child arrangements application C100 - %s", CommonUtils.formatLocalDateTime(caseData.getCaseSubmittedTimeStamp(), DATE_TIME_OF_SUBMISSION_FORMAT)); @@ -236,7 +240,8 @@ public Map populateHwfDynamicData(CaseDetails caseDetails) { CaseData caseData = CaseUtils.getCaseData(caseDetails, objectMapper); Map caseDataUpdated = caseDetails.getData(); log.info("case state :" + caseDetails.getState()); - if (State.SUBMITTED_NOT_PAID.getValue().equalsIgnoreCase(caseDetails.getState())) { + if (State.SUBMITTED_NOT_PAID.getValue().equalsIgnoreCase(caseDetails.getState()) + || State.AWAITING_INFORMATION.getValue().equalsIgnoreCase(caseDetails.getState())) { log.info("populate data for C100 application"); caseDataUpdated.put(HWF_APPLICATION_DYNAMIC_DATA_LABEL, String.format( diff --git a/src/main/java/uk/gov/hmcts/reform/prl/services/RequestFurtherInformationService.java b/src/main/java/uk/gov/hmcts/reform/prl/services/RequestFurtherInformationService.java new file mode 100644 index 00000000000..a071116ed7f --- /dev/null +++ b/src/main/java/uk/gov/hmcts/reform/prl/services/RequestFurtherInformationService.java @@ -0,0 +1,142 @@ +package uk.gov.hmcts.reform.prl.services; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Builder; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import uk.gov.hmcts.reform.ccd.client.model.CallbackRequest; +import uk.gov.hmcts.reform.ccd.client.model.CaseDataContent; +import uk.gov.hmcts.reform.ccd.client.model.CaseDetails; +import uk.gov.hmcts.reform.ccd.client.model.Event; +import uk.gov.hmcts.reform.ccd.client.model.EventRequestData; +import uk.gov.hmcts.reform.prl.clients.ccd.CcdCoreCaseDataService; +import uk.gov.hmcts.reform.prl.models.complextypes.tab.summarytab.summary.CaseStatus; +import uk.gov.hmcts.reform.prl.models.dto.ccd.RequestFurtherInformation; +import uk.gov.hmcts.reform.prl.utils.CaseUtils; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static java.lang.String.valueOf; +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.joining; +import static org.apache.commons.lang3.StringUtils.SPACE; +import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.CASE_STATUS; +import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.REQUEST_FURTHER_INFORMATION_DETAILS; +import static uk.gov.hmcts.reform.prl.enums.CaseEvent.REQUEST_FURTHER_INFORMATION_HISTORY; +import static uk.gov.hmcts.reform.prl.enums.State.AWAITING_INFORMATION; + +@Slf4j +@Builder +@RequiredArgsConstructor +@Service +public class RequestFurtherInformationService { + + private final FeatureToggleService featureToggleService; + private final ObjectMapper objectMapper; + private final CcdCoreCaseDataService ccdCoreCaseDataService; + private final SystemUserService systemUserService; + + public List validate(CallbackRequest callbackRequest) { + if (!featureToggleService.isAwaitingInformationEnabled()) { + return emptyList(); + } + var caseDataUpdated = addToCase(callbackRequest); + var requestFurtherInformation = getRequestFurtherInformation(caseDataUpdated); + var errorList = new ArrayList(); + if (requestFurtherInformation.getReviewDate() + != null && !requestFurtherInformation.getReviewDate().isAfter(LocalDate.now())) { + errorList.add("Please enter a future date"); + } + return errorList; + } + + public Map addToCase(CallbackRequest callbackRequest) { + Map caseDataUpdated = callbackRequest.getCaseDetails().getData(); + caseDataUpdated.put( + CASE_STATUS, CaseStatus.builder() + .state(AWAITING_INFORMATION.getLabel()) + .build() + ); + CaseUtils.setCaseState(callbackRequest, caseDataUpdated); + return caseDataUpdated; + } + + private RequestFurtherInformation getRequestFurtherInformation(Map caseDataUpdated) { + var requestFurtherInformation = objectMapper.convertValue( + caseDataUpdated.get(REQUEST_FURTHER_INFORMATION_DETAILS), + RequestFurtherInformation.class + ); + return requestFurtherInformation; + } + + /** + * Builds event description from RequestFurtherInformation data. + * Combines reviewDate and requestFurtherInformationReasonEnum into a formatted string. + * + * @param requestFurtherInformation The RequestFurtherInformation object + * @return Formatted event description string + */ + public Event buildEventWithDescription(RequestFurtherInformation requestFurtherInformation) { + StringBuilder description = new StringBuilder(); + + // Add review date with DD/Month/YYYY format + if (requestFurtherInformation.getReviewDate() != null) { + DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd MMM yyyy"); + String formattedDate = requestFurtherInformation.getReviewDate().format(dateFormatter); + description.append("Review By Date: ") + .append(formattedDate) + .append(SPACE) + .append(".\n"); + } + + // Add reasons + var reasons = requestFurtherInformation.getRequestFurtherInformationReasonEnum(); + if (reasons != null && !reasons.isEmpty()) { + // Add newline if something already exists + if (description.length() > 0) { + description.append("\n"); + } + description.append("Awaiting Information Reasons:\n"); + // Join all displayed values with newlines + String reasonText = reasons.stream() + .map(r -> r.getDisplayedValue()) + .collect(joining(", \n")); + description.append(reasonText); + } + + // Create Event with description + return Event.builder() + .id(REQUEST_FURTHER_INFORMATION_HISTORY.getValue()) + .description(description.toString()) + .build(); + } + + public CaseDetails updateHistoryTab(CallbackRequest callbackRequest) { + String systemAuthToken = systemUserService.getSysUserToken(); + String systemUpdateUserId = systemUserService.getUserId(systemAuthToken); + + EventRequestData eventRequestData = ccdCoreCaseDataService.eventRequest( + REQUEST_FURTHER_INFORMATION_HISTORY, systemUpdateUserId); + + String caseId = valueOf(callbackRequest.getCaseDetails().getId()); + var startEventResponse = + ccdCoreCaseDataService.startUpdate(systemAuthToken, eventRequestData, caseId, true); + var requestFurtherInformation = getRequestFurtherInformation(startEventResponse.getCaseDetails().getData()); + + var caseDataContent = CaseDataContent.builder() + .eventToken(startEventResponse.getToken()) + .event(buildEventWithDescription(requestFurtherInformation)) + .data(startEventResponse.getCaseDetails().getData()) + .build(); + CaseDetails caseDetails = ccdCoreCaseDataService.submitUpdate( + systemAuthToken, eventRequestData, caseDataContent, caseId, true); + log.info("History tab updated for case id: {}", caseId); + return caseDetails; + } + +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 809271cc2a9..0a808a79512 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -81,6 +81,7 @@ feature: barristerFeatureEnabled: ${BARRISTER_FEATURE_ENABLED:false} cafcassDateTimeFeatureEnabled: ${CAFCASS_DATE_TIME_FEATURE_ENABLED:false} osCourtLookupEnabled: ${OS_COURT_LOOKUP_ENABLED:false} + awaitingInformationEnabled: ${AWAITING_INFORMATION_ENABLED:false} xui: url: ${XUI_URL:https://manage-case.aat.platform.hmcts.net/cases/case-details} diff --git a/src/test/java/uk/gov/hmcts/reform/prl/controllers/CallbackControllerTest.java b/src/test/java/uk/gov/hmcts/reform/prl/controllers/CallbackControllerTest.java index 70a496c3b18..4ccdedda793 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/controllers/CallbackControllerTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/controllers/CallbackControllerTest.java @@ -150,6 +150,7 @@ import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.ISSUED_STATE; import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.ROLES; import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.STAFF; +import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.STATE_FIELD; import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.SUBMITTED_STATE; import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.TASK_LIST_VERSION_V2; import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.TASK_LIST_VERSION_V3; @@ -166,6 +167,8 @@ import static uk.gov.hmcts.reform.prl.enums.OrderTypeEnum.childArrangementsOrder; import static uk.gov.hmcts.reform.prl.enums.RelationshipsEnum.father; import static uk.gov.hmcts.reform.prl.enums.RelationshipsEnum.specialGuardian; +import static uk.gov.hmcts.reform.prl.enums.State.AWAITING_INFORMATION; +import static uk.gov.hmcts.reform.prl.enums.State.CASE_ISSUED; import static uk.gov.hmcts.reform.prl.enums.State.SUBMITTED_PAID; import static uk.gov.hmcts.reform.prl.enums.YesOrNo.No; import static uk.gov.hmcts.reform.prl.enums.YesOrNo.Yes; @@ -1186,6 +1189,35 @@ public void testAddCaseNumberStateSubmittedPaid() throws Exception { assertEquals(Yes.getDisplayedValue(),aboutToStartOrSubmitCallbackResponse.getData().get("isAddCaseNumberAdded")); } + @Test + public void testAddCaseNumberStateAwaitingInformation() throws Exception { + + CaseData caseData = CaseData.builder() + .issueDate(LocalDate.now()) + .build(); + + Map stringObjectMap = new HashMap<>(); + stringObjectMap.put(STATE_FIELD, AWAITING_INFORMATION); + when(objectMapper.convertValue(stringObjectMap, CaseData.class)).thenReturn(caseData); + when(userService.getUserDetails(Mockito.anyString())).thenReturn(userDetails); + when(caseEventService.findEventsForCase("1")) + .thenReturn(List.of(CaseEventDetail.builder().stateId( + AWAITING_INFORMATION.getValue()).build())); + CallbackRequest callbackRequest = uk.gov.hmcts.reform.ccd.client.model + .CallbackRequest.builder() + .caseDetails(uk.gov.hmcts.reform.ccd.client.model.CaseDetails.builder() + .id(1L) + .data(stringObjectMap).build()).build(); + when(authorisationService.isAuthorized(any(),any())).thenReturn(true); + when(caseSummaryTab.updateTab(caseData.toBuilder().state(CASE_ISSUED).build())).thenReturn(Map.of()); + AboutToStartOrSubmitCallbackResponse aboutToStartOrSubmitCallbackResponse = callbackController + .addCaseNumberSubmitted(authToken, s2sToken, callbackRequest); + assertNotNull(aboutToStartOrSubmitCallbackResponse.getData().get("issueDate")); + assertEquals(Yes.getDisplayedValue(), aboutToStartOrSubmitCallbackResponse.getData().get("isAddCaseNumberAdded")); + assertEquals(CASE_ISSUED.getValue(), aboutToStartOrSubmitCallbackResponse.getData().get(STATE_FIELD)); + verify(caseSummaryTab).updateTab(caseData.toBuilder().state(CASE_ISSUED).build()); + } + @Test public void aboutToSubmitCaseCreationToC100ForNullCaseNameWithException() { diff --git a/src/test/java/uk/gov/hmcts/reform/prl/controllers/RequestFurtherInformationControllerTest.java b/src/test/java/uk/gov/hmcts/reform/prl/controllers/RequestFurtherInformationControllerTest.java new file mode 100644 index 00000000000..9d300ed09a5 --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/prl/controllers/RequestFurtherInformationControllerTest.java @@ -0,0 +1,385 @@ +package uk.gov.hmcts.reform.prl.controllers; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import uk.gov.hmcts.reform.ccd.client.model.AboutToStartOrSubmitCallbackResponse; +import uk.gov.hmcts.reform.ccd.client.model.CallbackRequest; +import uk.gov.hmcts.reform.ccd.client.model.CaseDetails; +import uk.gov.hmcts.reform.prl.models.complextypes.tab.summarytab.summary.CaseStatus; +import uk.gov.hmcts.reform.prl.models.dto.ccd.CallbackResponse; +import uk.gov.hmcts.reform.prl.services.AuthorisationService; +import uk.gov.hmcts.reform.prl.services.FeatureToggleService; +import uk.gov.hmcts.reform.prl.services.RequestFurtherInformationService; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.CASE_STATUS; + +@RunWith(MockitoJUnitRunner.Silent.class) +public class RequestFurtherInformationControllerTest { + + @InjectMocks + private RequestFurtherInformationController requestFurtherInformationController; + + @Mock + private RequestFurtherInformationService requestFurtherInformationService; + + @Mock + private FeatureToggleService featureToggleService; + + @Mock + private AuthorisationService authorisationService; + + @Mock + private ObjectMapper objectMapper; + + private static final String AUTH_TOKEN = "Bearer testAuthToken"; + private static final String S2S_TOKEN = "s2sTestToken"; + + private CallbackRequest callbackRequest; + private CaseDetails caseDetails; + private Map caseDataMap; + private Map updatedCaseData; + + @Before + public void setUp() { + caseDataMap = new HashMap<>(); + caseDataMap.put("id", 12345678L); + + caseDetails = CaseDetails.builder() + .id(12345678L) + .state("AWAITING_INFORMATION") + .data(caseDataMap) + .build(); + + callbackRequest = CallbackRequest.builder() + .caseDetails(caseDetails) + .build(); + + updatedCaseData = new HashMap<>(caseDataMap); + updatedCaseData.put(CASE_STATUS, CaseStatus.builder().state("Awaiting information").build()); + + when(featureToggleService.isAwaitingInformationEnabled()).thenReturn(true); + } + + // Tests for submitAwaitingInformation endpoint + + @Test + public void shouldSubmitAwaitingInformationSuccessfully() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + when(requestFurtherInformationService.addToCase(callbackRequest)).thenReturn(updatedCaseData); + + AboutToStartOrSubmitCallbackResponse response = requestFurtherInformationController + .submitAwaitingInformation(AUTH_TOKEN, S2S_TOKEN, callbackRequest); + + assertNotNull(response); + assertNotNull(response.getData()); + assertTrue(response.getData().containsKey(CASE_STATUS)); + verify(authorisationService, times(1)).isAuthorized(AUTH_TOKEN, S2S_TOKEN); + verify(requestFurtherInformationService, times(1)).addToCase(callbackRequest); + } + + @Test + public void shouldThrowExceptionWhenUnauthorized() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(false); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> + requestFurtherInformationController.submitAwaitingInformation(AUTH_TOKEN, S2S_TOKEN, callbackRequest)); + + assertEquals("Invalid Client", exception.getMessage()); + verify(authorisationService, times(1)).isAuthorized(AUTH_TOKEN, S2S_TOKEN); + verify(requestFurtherInformationService, times(0)).addToCase(any()); + } + + @Test + public void shouldThrowExceptionWhenFeatureToggleDisabled() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + when(featureToggleService.isAwaitingInformationEnabled()).thenReturn(false); + + RuntimeException exception = assertThrows(RuntimeException.class, () -> + requestFurtherInformationController.submitAwaitingInformation(AUTH_TOKEN, S2S_TOKEN, callbackRequest)); + + assertEquals("Invalid Client", exception.getMessage()); + verify(requestFurtherInformationService, times(0)).addToCase(any()); + } + + @Test + public void shouldPreserveExistingCaseDataWhenSubmitting() { + caseDataMap.put("applicantName", "John Doe"); + caseDataMap.put("respondentName", "Jane Doe"); + updatedCaseData.put("applicantName", "John Doe"); + updatedCaseData.put("respondentName", "Jane Doe"); + + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + when(requestFurtherInformationService.addToCase(callbackRequest)).thenReturn(updatedCaseData); + + AboutToStartOrSubmitCallbackResponse response = requestFurtherInformationController + .submitAwaitingInformation(AUTH_TOKEN, S2S_TOKEN, callbackRequest); + + assertNotNull(response.getData()); + assertEquals("John Doe", response.getData().get("applicantName")); + assertEquals("Jane Doe", response.getData().get("respondentName")); + } + + @Test + public void shouldHandleNullCaseData() { + Map emptyCaseData = new HashMap<>(); + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + when(requestFurtherInformationService.addToCase(callbackRequest)).thenReturn(emptyCaseData); + + AboutToStartOrSubmitCallbackResponse response = requestFurtherInformationController + .submitAwaitingInformation(AUTH_TOKEN, S2S_TOKEN, callbackRequest); + + assertNotNull(response); + assertNotNull(response.getData()); + } + + // Tests for validateReviewDate endpoint + + @Test + public void shouldValidateReviewDateSuccessfully() { + List emptyErrors = new ArrayList<>(); + when(requestFurtherInformationService.validate(callbackRequest)).thenReturn(emptyErrors); + + CallbackResponse response = requestFurtherInformationController.validateReviewDate(callbackRequest); + + assertNotNull(response); + assertNotNull(response.getErrors()); + assertTrue(response.getErrors().isEmpty()); + verify(requestFurtherInformationService, times(1)).validate(callbackRequest); + } + + @Test + public void shouldReturnValidationErrorsForInvalidDate() { + List errorList = new ArrayList<>(); + errorList.add("Please enter a future date"); + + when(requestFurtherInformationService.validate(callbackRequest)).thenReturn(errorList); + + CallbackResponse response = requestFurtherInformationController.validateReviewDate(callbackRequest); + + assertNotNull(response); + assertEquals(1, response.getErrors().size()); + assertEquals("Please enter a future date", response.getErrors().get(0)); + } + + @Test + public void shouldReturnEmptyErrorsWhenFeatureToggleDisabled() { + when(featureToggleService.isAwaitingInformationEnabled()).thenReturn(false); + + CallbackResponse response = requestFurtherInformationController.validateReviewDate(callbackRequest); + + assertNotNull(response); + assertTrue(response.getErrors().isEmpty()); + } + + @Test + public void shouldReturnMultipleValidationErrors() { + List errorList = new ArrayList<>(); + errorList.add("Please enter a future date"); + errorList.add("Review date cannot be more than 12 months away"); + + when(requestFurtherInformationService.validate(callbackRequest)).thenReturn(errorList); + + CallbackResponse response = requestFurtherInformationController.validateReviewDate(callbackRequest); + + assertNotNull(response); + assertEquals(2, response.getErrors().size()); + verify(requestFurtherInformationService, times(1)).validate(callbackRequest); + } + + @Test + public void shouldCallFeatureToggleServiceBeforeValidation() { + when(featureToggleService.isAwaitingInformationEnabled()).thenReturn(true); + when(requestFurtherInformationService.validate(callbackRequest)).thenReturn(new ArrayList<>()); + + CallbackResponse response = requestFurtherInformationController.validateReviewDate(callbackRequest); + + assertNotNull(response); + verify(featureToggleService, times(1)).isAwaitingInformationEnabled(); + } + + @Test + public void shouldHandleCaseDataWithMultipleFields() { + caseDataMap.put("caseType", "C100"); + caseDataMap.put("eventId", "123456"); + updatedCaseData.put("caseType", "C100"); + updatedCaseData.put("eventId", "123456"); + + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + when(requestFurtherInformationService.addToCase(callbackRequest)).thenReturn(updatedCaseData); + + AboutToStartOrSubmitCallbackResponse response = requestFurtherInformationController + .submitAwaitingInformation(AUTH_TOKEN, S2S_TOKEN, callbackRequest); + + assertNotNull(response.getData()); + assertEquals("C100", response.getData().get("caseType")); + assertEquals("123456", response.getData().get("eventId")); + } + + @Test + public void shouldVerifyCorrectHeadersUsedInSubmit() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + when(requestFurtherInformationService.addToCase(callbackRequest)).thenReturn(updatedCaseData); + + requestFurtherInformationController.submitAwaitingInformation(AUTH_TOKEN, S2S_TOKEN, callbackRequest); + + verify(authorisationService, times(1)).isAuthorized(eq(AUTH_TOKEN), eq(S2S_TOKEN)); + } + + + @Test + public void historyUpdated_ShouldSuccessfullyUpdateHistoryTab() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + + var response = requestFurtherInformationController.historyUpdated(AUTH_TOKEN, S2S_TOKEN, callbackRequest); + + assertNotNull(response); + assertEquals(200, response.getStatusCode().value()); + verify(requestFurtherInformationService, times(1)).updateHistoryTab(callbackRequest); + } + + @Test + public void historyUpdated_ThrowsExceptionWhenUnauthorized() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(false); + + assertThrows(RuntimeException.class, () -> + requestFurtherInformationController.historyUpdated(AUTH_TOKEN, S2S_TOKEN, callbackRequest)); + + verify(requestFurtherInformationService, times(0)).updateHistoryTab(any()); + } + + @Test + public void historyUpdated_ThrowsExceptionWhenFeatureToggleDisabled() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + when(featureToggleService.isAwaitingInformationEnabled()).thenReturn(false); + + assertThrows(RuntimeException.class, () -> + requestFurtherInformationController.historyUpdated(AUTH_TOKEN, S2S_TOKEN, callbackRequest)); + + verify(requestFurtherInformationService, times(0)).updateHistoryTab(any()); + } + + @Test + public void submitAwaitingInformation_WithValidTokens_Success() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + when(requestFurtherInformationService.addToCase(callbackRequest)).thenReturn(updatedCaseData); + + AboutToStartOrSubmitCallbackResponse response = requestFurtherInformationController + .submitAwaitingInformation(AUTH_TOKEN, S2S_TOKEN, callbackRequest); + + assertNotNull(response); + assertNotNull(response.getData()); + } + + @Test + public void validateReviewDate_FeatureToggleEnabled_CallsService() { + List emptyErrors = new ArrayList<>(); + when(featureToggleService.isAwaitingInformationEnabled()).thenReturn(true); + when(requestFurtherInformationService.validate(callbackRequest)).thenReturn(emptyErrors); + + CallbackResponse response = requestFurtherInformationController.validateReviewDate(callbackRequest); + + assertNotNull(response); + verify(requestFurtherInformationService, times(1)).validate(callbackRequest); + verify(featureToggleService, times(1)).isAwaitingInformationEnabled(); + } + + @Test + public void submitAwaitingInformation_AuthorizationFails_ThrowsException() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(false); + + assertThrows(RuntimeException.class, () -> + requestFurtherInformationController.submitAwaitingInformation(AUTH_TOKEN, S2S_TOKEN, callbackRequest)); + + verify(requestFurtherInformationService, times(0)).addToCase(callbackRequest); + } + + @Test + public void submitAwaitingInformation_FeatureToggleDisabled_ThrowsException() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + when(featureToggleService.isAwaitingInformationEnabled()).thenReturn(false); + + assertThrows(RuntimeException.class, () -> + requestFurtherInformationController.submitAwaitingInformation(AUTH_TOKEN, S2S_TOKEN, callbackRequest)); + + verify(requestFurtherInformationService, times(0)).addToCase(callbackRequest); + } + + @Test + public void validateReviewDate_MultipleErrors_ReturnsAllErrors() { + List errorList = new ArrayList<>(); + errorList.add("Error 1"); + errorList.add("Error 2"); + errorList.add("Error 3"); + + when(requestFurtherInformationService.validate(callbackRequest)).thenReturn(errorList); + + CallbackResponse response = requestFurtherInformationController.validateReviewDate(callbackRequest); + + assertEquals(3, response.getErrors().size()); + } + + @Test + public void submitAwaitingInformation_EmptyCaseData_ReturnsResponse() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + Map emptyCaseData = new HashMap<>(); + when(requestFurtherInformationService.addToCase(callbackRequest)).thenReturn(emptyCaseData); + + AboutToStartOrSubmitCallbackResponse response = requestFurtherInformationController + .submitAwaitingInformation(AUTH_TOKEN, S2S_TOKEN, callbackRequest); + + assertNotNull(response.getData()); + assertTrue(response.getData().isEmpty()); + } + + @Test + public void historyUpdated_WithValidTokens_ReturnsOkStatus() { + when(authorisationService.isAuthorized(AUTH_TOKEN, S2S_TOKEN)).thenReturn(true); + + var response = requestFurtherInformationController.historyUpdated(AUTH_TOKEN, S2S_TOKEN, callbackRequest); + + assertEquals(200, response.getStatusCode().value()); + } + + @Test + public void validateReviewDate_FeatureDisabledAndErrorsExist_ReturnsEmptyErrors() { + when(featureToggleService.isAwaitingInformationEnabled()).thenReturn(false); + + CallbackResponse response = requestFurtherInformationController.validateReviewDate(callbackRequest); + + assertTrue(response.getErrors().isEmpty()); + // Service should not be called when feature is disabled + verify(requestFurtherInformationService, times(0)).validate(any()); + } + + + @Test + public void validateReviewDate_NoErrorsReturned_ResponseIsEmpty() { + when(requestFurtherInformationService.validate(callbackRequest)).thenReturn(Collections.emptyList()); + + CallbackResponse response = requestFurtherInformationController.validateReviewDate(callbackRequest); + + assertNotNull(response); + assertNotNull(response.getErrors()); + assertEquals(0, response.getErrors().size()); + } +} diff --git a/src/test/java/uk/gov/hmcts/reform/prl/services/HelpWithFeesServiceTest.java b/src/test/java/uk/gov/hmcts/reform/prl/services/HelpWithFeesServiceTest.java index 879842d445c..4efda9e4dc2 100644 --- a/src/test/java/uk/gov/hmcts/reform/prl/services/HelpWithFeesServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/prl/services/HelpWithFeesServiceTest.java @@ -94,6 +94,21 @@ public void testAboutToStart() { assertEquals("C100",response.get("caseTypeOfApplication")); } + @Test + public void testAboutToStartAwaitingInformation() { + CaseDetails awaitingInfoCaseDetails = CaseDetails.builder() + .id(123L) + .state(State.AWAITING_INFORMATION.getValue()) + .build(); + + when(objectMapper.convertValue(awaitingInfoCaseDetails.getData(), CaseData.class)).thenReturn(casedata); + Map response = helpWithFeesService.handleAboutToStart(awaitingInfoCaseDetails); + assertNotNull(response); + DynamicList dynamicList = (DynamicList) response.get("hwfAppList"); + assertEquals("Child arrangements application C100 - 24/06/2024 10:46:55", dynamicList.getListItems().get(0).getLabel()); + assertEquals("C100", response.get("caseTypeOfApplication")); + } + @Test public void testAboutToSubmitWithoutDateSubmitted() { casedata = casedata.toBuilder() @@ -136,6 +151,48 @@ public void testAboutToSubmitWithDateSubmitted() { assertNull(response.get("dateSubmitted")); } + @Test + public void testAboutToSubmitAwaitingInformationWithoutDateSubmitted() { + casedata = casedata.toBuilder() + .state(State.AWAITING_INFORMATION) + .build(); + + CaseDetails awaitingInfoCaseDetails = CaseDetails.builder() + .id(123L) + .state(State.AWAITING_INFORMATION.getValue()) + .data(new HashMap<>()) + .build(); + + when(objectMapper.convertValue(awaitingInfoCaseDetails.getData(), CaseData.class)).thenReturn(casedata); + Map response = helpWithFeesService + .setCaseStatus(CallbackRequest.builder().caseDetails(awaitingInfoCaseDetails).build(), "testAuth"); + assertNotNull(response); + CaseStatus caseStatus = (CaseStatus) response.get("caseStatus"); + assertEquals("Submitted", caseStatus.getState()); + assertNotNull(response.get("dateSubmitted")); + } + + @Test + public void testAboutToSubmitAwaitingInformationWithDateSubmitted() { + casedata = casedata.toBuilder() + .state(State.AWAITING_INFORMATION) + .dateSubmitted("01 01 2024") + .build(); + + CaseDetails awaitingInfoCaseDetails = CaseDetails.builder() + .id(123L) + .state(State.AWAITING_INFORMATION.getValue()) + .data(new HashMap<>()) + .build(); + + when(objectMapper.convertValue(awaitingInfoCaseDetails.getData(), CaseData.class)).thenReturn(casedata); + Map response = helpWithFeesService + .setCaseStatus(CallbackRequest.builder().caseDetails(awaitingInfoCaseDetails).build(), "testAuth"); + assertNotNull(response); + CaseStatus caseStatus = (CaseStatus) response.get("caseStatus"); + assertEquals("Submitted", caseStatus.getState()); + assertNull(response.get("dateSubmitted")); + } @Test public void testAboutToSubmitApplicationsWithinProceedingsProcessUrgentFeesIsNull() { @@ -279,6 +336,20 @@ public void testPopulateHwfDynamicData() { assertNotNull(hwfApplicationDynamicData); } + @Test + public void testPopulateHwfDynamicDataAwaitingInformation() { + CaseDetails awaitingInfoCaseDetails = CaseDetails.builder() + .id(123L) + .state(State.AWAITING_INFORMATION.getValue()) + .data(casedata.toMap(new ObjectMapper())) + .build(); + when(objectMapper.convertValue(awaitingInfoCaseDetails.getData(), CaseData.class)).thenReturn(casedata); + Map response = helpWithFeesService.populateHwfDynamicData(awaitingInfoCaseDetails); + assertNotNull(response); + String hwfApplicationDynamicData = (String) response.get(HWF_APPLICATION_DYNAMIC_DATA_LABEL); + assertNotNull(hwfApplicationDynamicData); + } + @Test public void testPopulateHwfDynamicData_c2Awp() { UUID applicationId = UUID.randomUUID(); diff --git a/src/test/java/uk/gov/hmcts/reform/prl/services/RequestFurtherInformationServiceTest.java b/src/test/java/uk/gov/hmcts/reform/prl/services/RequestFurtherInformationServiceTest.java new file mode 100644 index 00000000000..fcd3559288d --- /dev/null +++ b/src/test/java/uk/gov/hmcts/reform/prl/services/RequestFurtherInformationServiceTest.java @@ -0,0 +1,214 @@ +package uk.gov.hmcts.reform.prl.services; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import uk.gov.hmcts.reform.ccd.client.model.CallbackRequest; +import uk.gov.hmcts.reform.ccd.client.model.CaseDetails; +import uk.gov.hmcts.reform.ccd.client.model.Event; +import uk.gov.hmcts.reform.prl.enums.awaitinginformation.RequestFurtherInformationReasonEnum; +import uk.gov.hmcts.reform.prl.models.dto.ccd.RequestFurtherInformation; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; +import static uk.gov.hmcts.reform.prl.constants.PrlAppsConstants.REQUEST_FURTHER_INFORMATION_DETAILS; +import static uk.gov.hmcts.reform.prl.enums.State.AWAITING_INFORMATION; + +@RunWith(MockitoJUnitRunner.Silent.class) +public class RequestFurtherInformationServiceTest { + + @InjectMocks + private RequestFurtherInformationService requestFurtherInformationService; + + @Mock + private FeatureToggleService featureToggleService; + + @Mock + private ObjectMapper objectMapper; + + @Mock + private CallbackRequest callbackRequest; + + private Map caseDataMap; + private CaseDetails caseDetails; + + @Before + public void setUp() { + caseDataMap = new HashMap<>(); + caseDataMap.put("id", 12345678L); + + caseDetails = CaseDetails.builder() + .id(12345678L) + .state(AWAITING_INFORMATION.getLabel()) + .data(caseDataMap) + .build(); + + callbackRequest = CallbackRequest.builder() + .caseDetails(caseDetails) + .build(); + + when(featureToggleService.isAwaitingInformationEnabled()).thenReturn(true); + } + + @Test + public void validate_FutureDate_NoErrors() { + RequestFurtherInformation awaitingInfo = RequestFurtherInformation.builder() + .reviewDate(LocalDate.now().plusDays(10)) + .build(); + + caseDataMap.put(REQUEST_FURTHER_INFORMATION_DETAILS, awaitingInfo); + when(objectMapper.convertValue(awaitingInfo, RequestFurtherInformation.class)) + .thenReturn(awaitingInfo); + + List errors = requestFurtherInformationService.validate(callbackRequest); + + assertTrue(errors.isEmpty()); + } + + @Test + public void validate_PastDate_ReturnsError() { + RequestFurtherInformation awaitingInfo = RequestFurtherInformation.builder() + .reviewDate(LocalDate.now().minusDays(1)) + .build(); + + caseDataMap.put(REQUEST_FURTHER_INFORMATION_DETAILS, awaitingInfo); + when(objectMapper.convertValue(awaitingInfo, RequestFurtherInformation.class)) + .thenReturn(awaitingInfo); + + List errors = requestFurtherInformationService.validate(callbackRequest); + + assertFalse(errors.isEmpty()); + } + + @Test + public void buildEventWithDescription_WithSingleReason() { + RequestFurtherInformation info = RequestFurtherInformation.builder() + .reviewDate(LocalDate.of(2026, 3, 28)) + .requestFurtherInformationReasonEnum(Collections.singletonList( + RequestFurtherInformationReasonEnum.miamFurtherInformation + )) + .build(); + + Event event = requestFurtherInformationService.buildEventWithDescription(info); + + assertTrue(event.getDescription().contains("28 Mar 2026")); + assertTrue(event.getDescription().contains("MIAM - further information required")); + } + + @Test + public void buildEventWithDescription_WithMultipleReasons() { + RequestFurtherInformation info = RequestFurtherInformation.builder() + .reviewDate(LocalDate.of(2026, 3, 28)) + .requestFurtherInformationReasonEnum(Arrays.asList( + RequestFurtherInformationReasonEnum.miamFurtherInformation, + RequestFurtherInformationReasonEnum.dwpHmrcWhereaboutsUnknown + )) + .build(); + + Event event = requestFurtherInformationService.buildEventWithDescription(info); + + assertTrue(event.getDescription().contains("28 Mar 2026")); + assertTrue(event.getDescription().contains("MIAM - further information required")); + assertTrue(event.getDescription().contains("DWP/HMRC - whereabouts unknown")); + } + + @Test + public void buildEventWithDescription_DifferentMonths() { + RequestFurtherInformation jan = RequestFurtherInformation.builder() + .reviewDate(LocalDate.of(2026, 1, 5)) + .requestFurtherInformationReasonEnum(Collections.singletonList( + RequestFurtherInformationReasonEnum.miamFurtherInformation + )) + .build(); + + Event janEvent = requestFurtherInformationService.buildEventWithDescription(jan); + assertTrue(janEvent.getDescription().contains("05 Jan 2026")); + } + + @Test + public void buildEventWithDescription_NullReviewDate() { + RequestFurtherInformation info = RequestFurtherInformation.builder() + .reviewDate(null) + .requestFurtherInformationReasonEnum(Collections.singletonList( + RequestFurtherInformationReasonEnum.miamFurtherInformation + )) + .build(); + + Event event = requestFurtherInformationService.buildEventWithDescription(info); + + assertFalse(event.getDescription().contains("Review By Date:")); + assertTrue(event.getDescription().contains("MIAM - further information required")); + } + + @Test + public void buildEventWithDescription_NullReasons() { + RequestFurtherInformation info = RequestFurtherInformation.builder() + .reviewDate(LocalDate.of(2026, 3, 28)) + .requestFurtherInformationReasonEnum(null) + .build(); + + Event event = requestFurtherInformationService.buildEventWithDescription(info); + + assertTrue(event.getDescription().contains("28 Mar 2026")); + assertFalse(event.getDescription().contains("Awaiting Information Reasons:")); + } + + @Test + public void buildEventWithDescription_EmptyReasons() { + RequestFurtherInformation info = RequestFurtherInformation.builder() + .reviewDate(LocalDate.of(2026, 3, 28)) + .requestFurtherInformationReasonEnum(Collections.emptyList()) + .build(); + + Event event = requestFurtherInformationService.buildEventWithDescription(info); + + assertTrue(event.getDescription().contains("28 Mar 2026")); + } + + @Test + public void buildEventWithDescription_SingleDigitDay() { + RequestFurtherInformation info = RequestFurtherInformation.builder() + .reviewDate(LocalDate.of(2026, 3, 5)) + .requestFurtherInformationReasonEnum(Collections.singletonList( + RequestFurtherInformationReasonEnum.miamFurtherInformation + )) + .build(); + + Event event = requestFurtherInformationService.buildEventWithDescription(info); + + assertTrue(event.getDescription().contains("05 Mar 2026")); + } + + @Test + public void buildEventWithDescription_MultipleReasonTypes() { + RequestFurtherInformation info = RequestFurtherInformation.builder() + .reviewDate(LocalDate.of(2026, 5, 20)) + .requestFurtherInformationReasonEnum(Arrays.asList( + RequestFurtherInformationReasonEnum.miamFurtherInformation, + RequestFurtherInformationReasonEnum.dwpHmrcWhereaboutsUnknown, + RequestFurtherInformationReasonEnum.other + )) + .build(); + + Event event = requestFurtherInformationService.buildEventWithDescription(info); + + String desc = event.getDescription(); + assertTrue(desc.contains("20 May 2026")); + assertTrue(desc.contains("MIAM - further information required")); + assertTrue(desc.contains("DWP/HMRC - whereabouts unknown")); + assertTrue(desc.contains("Another reason that has not been listed")); + } +} +