diff --git a/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/TermsAndConditionsFT.java b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/TermsAndConditionsFT.java new file mode 100644 index 0000000000..69467cb4cf --- /dev/null +++ b/src/functionalTest/java/uk/gov/hmcts/reform/preapi/controllers/TermsAndConditionsFT.java @@ -0,0 +1,44 @@ +package uk.gov.hmcts.reform.preapi.controllers; + +import io.restassured.response.Response; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import uk.gov.hmcts.reform.preapi.controllers.params.TestingSupportRoles; +import uk.gov.hmcts.reform.preapi.util.FunctionalTestBase; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +public class TermsAndConditionsFT extends FunctionalTestBase { + + @Test + @DisplayName("Should return the latest APP terms when the feature flag is enabled") + void getLatestAppTerms() { + // Create an APP terms and conditions record for testing + Response created = doPostRequest("/testing-support/create-terms-and-conditions/APP", + TestingSupportRoles.SUPER_USER); + assertResponseCode(created, 200); + String createdId = created.body().jsonPath().getString("termsId"); + + // Get latest APP terms and conditions + Response response = doGetRequest("/app-terms-and-conditions/latest", TestingSupportRoles.SUPER_USER); + assertResponseCode(response, 200); + assertThat(response.body().jsonPath().getString("id")).isEqualTo(createdId); + assertThat(response.body().jsonPath().getString("type")).isEqualTo("APP"); + } + + @Test + @DisplayName("Should return the latest Portal terms") + void getLatestPortalTerms() { + // Create a Portal terms and conditions record for testing + Response created = doPostRequest("/testing-support/create-terms-and-conditions/PORTAL", + TestingSupportRoles.SUPER_USER); + assertResponseCode(created, 200); + String createdId = created.body().jsonPath().getString("termsId"); + + // Get latest Portal terms and conditions + Response response = doGetRequest("/portal-terms-and-conditions/latest", TestingSupportRoles.SUPER_USER); + assertResponseCode(response, 200); + assertThat(response.body().jsonPath().getString("id")).isEqualTo(createdId); + assertThat(response.body().jsonPath().getString("type")).isEqualTo("PORTAL"); + } +} diff --git a/src/functionalTest/resources/application.properties b/src/functionalTest/resources/application.properties index 1c0894c0e2..3c5bf455bd 100644 --- a/src/functionalTest/resources/application.properties +++ b/src/functionalTest/resources/application.properties @@ -24,6 +24,9 @@ mediakind.symmetricKey=${SYMMETRIC_KEY:} media-service=${MEDIA_SERVICE:MediaKind} editing.enable=true +feature-flags.dynatrace-terms-and-conditions.enabled=true +feature-flags.dynatrace-terms-and-conditions.cut-off-date=2026-07-15 + #logging.level.uk.gov.hmcts.reform.preapi.actuator.PreApiHealthIndicator: DEBUG #logging.level.uk.gov.hmcts.reform.preapi.email.StopLiveEventNotifierFlowClient: DEBUG #logging.level.uk.gov.hmcts.reform.preapi.email.CaseStateChangeNotifierFlowClient: DEBUG diff --git a/src/integrationTest/java/uk/gov/hmcts/reform/preapi/services/TermsAndConditionsServiceCutoffUsedIT.java b/src/integrationTest/java/uk/gov/hmcts/reform/preapi/services/TermsAndConditionsServiceCutoffUsedIT.java new file mode 100644 index 0000000000..14c8292103 --- /dev/null +++ b/src/integrationTest/java/uk/gov/hmcts/reform/preapi/services/TermsAndConditionsServiceCutoffUsedIT.java @@ -0,0 +1,83 @@ +package uk.gov.hmcts.reform.preapi.services; + +import jakarta.transaction.Transactional; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.TestPropertySource; +import uk.gov.hmcts.reform.preapi.dto.TermsAndConditionsDTO; +import uk.gov.hmcts.reform.preapi.entities.TermsAndConditions; +import uk.gov.hmcts.reform.preapi.enums.TermsAndConditionsType; +import uk.gov.hmcts.reform.preapi.util.HelperFactory; +import uk.gov.hmcts.reform.preapi.utils.IntegrationTestBase; + +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.assertThat; + +@TestPropertySource(properties = { + "feature-flags.dynatrace-terms-and-conditions.enabled=false", + "feature-flags.dynatrace-terms-and-conditions.cut-off-date=2026-07-15" +}) +public class TermsAndConditionsServiceCutoffUsedIT extends IntegrationTestBase { + + @Autowired + private TermsAndConditionsService termsAndConditionsService; + + @Test + @Transactional + public void getLatestAppTermsAndConditionsCreatedBeforeCutoff() { + //Create an app terms that is before cutoff date + TermsAndConditions termsCreatedBeforeCutoff = + HelperFactory.createTermsAndConditions(TermsAndConditionsType.APP, "
July 2026
\n\nThe PRE Service provides access to pre-recorded cross examination recordings. It is supplied to individual users (The User) in accordance with the following Terms & Conditions.
\nThe User understands that use of the Service will be taken as their acceptance of these Terms & Conditions and that they are fully aware of their responsibilities in relation to the use of the service as set out in the Terms & Conditions on the page below.
\n\n\nWe use cookies to help us understand how the service is being used and to improve it. Cookies we use:
\n| Name | \nPurpose | \nExpires | \n
|---|---|---|
| dtCookie dtPC dtSa rxVisitor rxtv | \nPlease see the Dynatrace website for up-to-date information on the usage of each of these cookies. | \nPlease see the Dynatrace website for up-to-date information on the expiry of these cookies. | \n
Declaration:
\nI declare that I am fully aware of my responsibilities in relation to the use of the Service as set out in the above Terms & Conditions.
', + 'APP' +); diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/services/TermsAndConditionsServiceTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/services/TermsAndConditionsServiceTest.java index 1372ea3399..63fc3a13dd 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/services/TermsAndConditionsServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/services/TermsAndConditionsServiceTest.java @@ -1,10 +1,12 @@ package uk.gov.hmcts.reform.preapi.services; +import org.junit.jupiter.api.AfterEach; 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.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.util.ReflectionTestUtils; import uk.gov.hmcts.reform.preapi.enums.TermsAndConditionsType; import uk.gov.hmcts.reform.preapi.exception.NotFoundException; import uk.gov.hmcts.reform.preapi.repositories.TermsAndConditionsRepository; @@ -12,15 +14,23 @@ import java.sql.Timestamp; import java.time.Instant; +import java.time.LocalDate; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +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 = TermsAndConditionsService.class) +@SpringBootTest(classes = TermsAndConditionsService.class, + properties = { + "feature-flags.dynatrace-terms-and-conditions.enabled=true", + "feature-flags.dynatrace-terms-and-conditions.cut-off-date=2026-07-15" + }) public class TermsAndConditionsServiceTest { @MockitoBean private TermsAndConditionsRepository termsAndConditionsRepository; @@ -28,10 +38,18 @@ public class TermsAndConditionsServiceTest { @Autowired private TermsAndConditionsService termsAndConditionsService; + @AfterEach + void resetTermsFeatureFlags() { + ReflectionTestUtils.setField(termsAndConditionsService, "isDynatraceAppTermsEnabled", true); + ReflectionTestUtils + .setField(termsAndConditionsService, "cutOffDate", LocalDate.of(2026, 7, 15)); + } + @Test @DisplayName("Should get the latest app terms and conditions and return model") public void getLatestAppTermsAndConditionsSuccess() { - var termsAndConditions = HelperFactory.createTermsAndConditions(TermsAndConditionsType.APP, "some content"); + var termsAndConditions = + HelperFactory.createTermsAndConditions(TermsAndConditionsType.APP, "some content"); termsAndConditions.setCreatedAt(Timestamp.from(Instant.now())); when(termsAndConditionsRepository.findFirstByTypeOrderByCreatedAtDesc(TermsAndConditionsType.APP)) @@ -44,13 +62,15 @@ public void getLatestAppTermsAndConditionsSuccess() { assertThat(model.getHtml()).isEqualTo(termsAndConditions.getContent()); assertThat(model.getCreatedAt()).isEqualTo(termsAndConditions.getCreatedAt()); - verify(termsAndConditionsRepository, times(1)).findFirstByTypeOrderByCreatedAtDesc(TermsAndConditionsType.APP); + verify(termsAndConditionsRepository, + times(1)).findFirstByTypeOrderByCreatedAtDesc(TermsAndConditionsType.APP); } @Test @DisplayName("Should get the latest portal terms and conditions and return model") public void getLatestPortalTermsAndConditionsSuccess() { - var termsAndConditions = HelperFactory.createTermsAndConditions(TermsAndConditionsType.PORTAL, "some content"); + var termsAndConditions = + HelperFactory.createTermsAndConditions(TermsAndConditionsType.PORTAL, "some content"); termsAndConditions.setCreatedAt(Timestamp.from(Instant.now())); when(termsAndConditionsRepository.findFirstByTypeOrderByCreatedAtDesc(TermsAndConditionsType.PORTAL)) @@ -79,7 +99,75 @@ public void getLatestTermsAndConditionsNotFound() { ).getMessage(); assertThat(message).isEqualTo("Not found: Terms and conditions of type: APP"); - verify(termsAndConditionsRepository, times(1)).findFirstByTypeOrderByCreatedAtDesc(TermsAndConditionsType.APP); + verify(termsAndConditionsRepository, + times(1)).findFirstByTypeOrderByCreatedAtDesc(TermsAndConditionsType.APP); + + } + + @Test + @DisplayName("Should use terms before cutoff for APP when dynatrace terms flag is disabled") + void appUsesCutoffDateWhenFlagDisabled() { + ReflectionTestUtils.setField(termsAndConditionsService, "isDynatraceAppTermsEnabled", false); + ReflectionTestUtils + .setField(termsAndConditionsService, "cutOffDate", LocalDate.of(2026, 7, 15)); + + var terms = HelperFactory.createTermsAndConditions(TermsAndConditionsType.APP, "some content"); + terms.setCreatedAt(Timestamp.from(Instant.now())); + + when(termsAndConditionsRepository.findFirstByTypeAndCreatedAtBeforeOrderByCreatedAtDesc( + eq(TermsAndConditionsType.APP), + eq(Timestamp.valueOf(LocalDate.of(2026, 7, 15).atStartOfDay())))) + .thenReturn(Optional.of(terms)); + + termsAndConditionsService.getLatestTermsAndConditions(TermsAndConditionsType.APP); + + verify(termsAndConditionsRepository, times(1)) + .findFirstByTypeAndCreatedAtBeforeOrderByCreatedAtDesc( + eq(TermsAndConditionsType.APP), any(Timestamp.class)); + verify(termsAndConditionsRepository, never()) + .findFirstByTypeOrderByCreatedAtDesc(TermsAndConditionsType.APP); + } + + @Test + @DisplayName("Should throw not found exception when there are no APP terms before cutoff date " + + "and dynatrace terms flag is disabled") + void appThrowsNotFoundWhenNoTermsBeforeCutoffAndFlagDisabled() { + ReflectionTestUtils.setField(termsAndConditionsService, "isDynatraceAppTermsEnabled", false); + ReflectionTestUtils + .setField(termsAndConditionsService, "cutOffDate", LocalDate.of(2026, 7, 15)); + + when(termsAndConditionsRepository.findFirstByTypeAndCreatedAtBeforeOrderByCreatedAtDesc( + eq(TermsAndConditionsType.APP), + eq(Timestamp.valueOf(LocalDate.of(2026, 7, 15).atStartOfDay())))) + .thenReturn(Optional.empty()); + var message = assertThrows( + NotFoundException.class, + () -> termsAndConditionsService.getLatestTermsAndConditions(TermsAndConditionsType.APP) + ).getMessage(); + assertThat(message).isEqualTo("Not found: Terms and conditions of type: APP"); + } + + @Test + @DisplayName("Portal should ignore dynatrace terms flag and always return the latest terms") + void portalDoesNotUseCutoffDateWhenFlagDisabled() { + ReflectionTestUtils.setField(termsAndConditionsService, "isDynatraceAppTermsEnabled", false); + ReflectionTestUtils + .setField(termsAndConditionsService, "cutOffDate", LocalDate.of(2026, 7, 15)); + + var terms = HelperFactory.createTermsAndConditions(TermsAndConditionsType.PORTAL, "some content"); + terms.setCreatedAt(Timestamp.from(Instant.now())); + + when(termsAndConditionsRepository.findFirstByTypeOrderByCreatedAtDesc( + eq(TermsAndConditionsType.PORTAL))) + .thenReturn(Optional.of(terms)); + + termsAndConditionsService.getLatestTermsAndConditions(TermsAndConditionsType.PORTAL); + + verify(termsAndConditionsRepository, never()) + .findFirstByTypeAndCreatedAtBeforeOrderByCreatedAtDesc( + any(TermsAndConditionsType.class), any(Timestamp.class)); + verify(termsAndConditionsRepository, times(1)) + .findFirstByTypeOrderByCreatedAtDesc(TermsAndConditionsType.PORTAL); } }