diff --git a/libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/flags/FeatureFlags.kt b/libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/flags/FeatureFlags.kt index f37766f91f..6e984e5c3c 100644 --- a/libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/flags/FeatureFlags.kt +++ b/libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/flags/FeatureFlags.kt @@ -2,16 +2,27 @@ package uk.gov.justice.digital.hmpps.flags import io.flipt.api.FliptClient import io.flipt.api.evaluation.models.EvaluationRequest +import org.slf4j.Logger +import org.slf4j.LoggerFactory import org.springframework.stereotype.Service @Service class FeatureFlags( private val client: FliptClient? ) { + companion object { + private val log: Logger = LoggerFactory.getLogger(this::class.java) + } + fun enabled(key: String) = try { - client == null || client.evaluation() - .evaluateBoolean(EvaluationRequest.builder().namespaceKey("probation-integration").flagKey(key).build()) - .isEnabled + if (client == null) { + log.warn("Flipt client not configured, all feature flags enabled.") + true + } else { + client.evaluation() + .evaluateBoolean(EvaluationRequest.builder().namespaceKey("probation-integration").flagKey(key).build()) + .isEnabled + } } catch (e: Exception) { throw FeatureFlagException(key, e) } diff --git a/projects/risk-assessment-scores-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/MessageGenerator.kt b/projects/risk-assessment-scores-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/MessageGenerator.kt index bb9567c43d..cb1d3372b9 100644 --- a/projects/risk-assessment-scores-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/MessageGenerator.kt +++ b/projects/risk-assessment-scores-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/MessageGenerator.kt @@ -5,6 +5,8 @@ import uk.gov.justice.digital.hmpps.resourceloader.ResourceLoader object MessageGenerator { val RSR_SCORES_DETERMINED = ResourceLoader.message("rsr-scores-determined") + val RSR_SCORES_DETERMINED_WITHOUT_OSPIIC_OSPDC = + ResourceLoader.message("rsr-scores-determined-null-osp") val OGRS_SCORES_DETERMINED = ResourceLoader.message("ogrs-scores-determined") val OGRS_SCORES_DETERMINED_UPDATE = ResourceLoader.message("ogrs-scores-determined-update") } diff --git a/projects/risk-assessment-scores-to-delius/src/dev/resources/db/h2.sql b/projects/risk-assessment-scores-to-delius/src/dev/resources/db/h2.sql index 1f3b5247c2..152fae0398 100644 --- a/projects/risk-assessment-scores-to-delius/src/dev/resources/db/h2.sql +++ b/projects/risk-assessment-scores-to-delius/src/dev/resources/db/h2.sql @@ -1,4 +1,16 @@ drop schema if exists pkg_triggersupport cascade; create schema pkg_triggersupport; -create alias pkg_triggersupport.procUpdateCAS as 'void stub(String p_crn, Integer p_event_number, java.util.Date p_rsr_assessor_date, Double p_rsr_score, String p_rsr_level_code, Double p_osp_score_i, Double p_osp_score_c, String p_osp_level_i_code, String p_osp_level_c_code) {}'; +create +alias pkg_triggersupport.procUpdateCAS as 'void stub(' || + 'String p_crn, ' || + 'Integer p_event_number, ' || + 'java.util.Date p_rsr_assessor_date, ' || + 'Double p_rsr_score, ' || + 'String p_rsr_level_code, ' || + 'Double p_osp_score_i, ' || + 'Double p_osp_score_c, ' || + 'String p_osp_level_i_code, ' || + 'String p_osp_level_c_code, ' || + 'String p_osp_level_iic_code, ' || + 'String p_osp_level_dc_code) {}'; diff --git a/projects/risk-assessment-scores-to-delius/src/dev/resources/db/oracle.sql b/projects/risk-assessment-scores-to-delius/src/dev/resources/db/oracle.sql index 4e47e9adc3..c08878accc 100644 --- a/projects/risk-assessment-scores-to-delius/src/dev/resources/db/oracle.sql +++ b/projects/risk-assessment-scores-to-delius/src/dev/resources/db/oracle.sql @@ -1,5 +1,3 @@ --- As of 30/09/2022, the procedure is not yet available in the Delius OracleDB so I've left this SQL here to help with local testing. - create or replace package pkg_triggersupport as procedure procUpdateCas( p_crn in varchar2, @@ -11,7 +9,9 @@ create or replace package pkg_triggersupport as p_osp_score_i in float, p_osp_level_i_code in varchar2, p_osp_score_c in float, - p_osp_level_c_code in varchar2 + p_osp_level_c_code in varchar2, + p_osp_level_iic_code in varchar2 default null, + p_osp_level_dc_code in varchar2 default null ); end pkg_triggersupport; grant execute on pkg_triggersupport to delius_app_schema; @@ -26,7 +26,9 @@ create or replace package body pkg_triggersupport as p_osp_score_i in float, p_osp_level_i_code in varchar2, p_osp_score_c in float, - p_osp_level_c_code in varchar2 + p_osp_level_c_code in varchar2, + p_osp_level_iic_code in varchar2 default null, + p_osp_level_dc_code in varchar2 default null ) is begin -- For testing: diff --git a/projects/risk-assessment-scores-to-delius/src/dev/resources/messages/rsr-scores-determined-null-osp.json b/projects/risk-assessment-scores-to-delius/src/dev/resources/messages/rsr-scores-determined-null-osp.json new file mode 100644 index 0000000000..b98b6e1680 --- /dev/null +++ b/projects/risk-assessment-scores-to-delius/src/dev/resources/messages/rsr-scores-determined-null-osp.json @@ -0,0 +1,17 @@ +{ + "Type": "Notification", + "MessageId": "b04098df-3d67-416f-87eb-f63782f713fb", + "TopicArn": "", + "Message": "{\"eventType\":\"risk-assessment.scores.rsr.determined\",\"version\":1,\"description\":\"Risk assessment scores have been determined\",\"detailUrl\":\"https://some-url-where-we-can-get-more-info-this-might-not-exist\",\"occurredAt\":\"2022-09-22T12:16:04+01:00\",\"additionalInformation\":{\"RSRScore\":45.33,\"RSRBand\":\"H\",\"RSRStaticOrDynamic\":\"STATIC\",\"OSPIndecentScore\":5.79,\"OSPIndecentBand\":\"H\",\"OSPContactScore\":38.7,\"OSPContactBand\":\"V\",\"EventNumber\":1,\"AssessmentDate\":\"2022-09-22T12:16:04+01:00\"},\"personReference\":{\"identifiers\":[{\"type\":\"CRN\",\"value\":\"X552020\"}]}}", + "Timestamp": "2022-05-04T08:06:46.704Z", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "", + "UnsubscribeURL": "", + "MessageAttributes": { + "eventType": { + "Type": "String", + "Value": "risk-assessment.scores.determined" + } + } +} \ No newline at end of file diff --git a/projects/risk-assessment-scores-to-delius/src/dev/resources/messages/rsr-scores-determined.json b/projects/risk-assessment-scores-to-delius/src/dev/resources/messages/rsr-scores-determined.json index 8fd8d5e9f8..a0d48d769e 100644 --- a/projects/risk-assessment-scores-to-delius/src/dev/resources/messages/rsr-scores-determined.json +++ b/projects/risk-assessment-scores-to-delius/src/dev/resources/messages/rsr-scores-determined.json @@ -2,7 +2,7 @@ "Type": "Notification", "MessageId": "b04098df-3d67-416f-87eb-f63782f713fb", "TopicArn": "", - "Message": "{\"eventType\":\"risk-assessment.scores.rsr.determined\",\"version\":1,\"description\":\"Risk assessment scores have been determined\",\"detailUrl\":\"https://some-url-where-we-can-get-more-info-this-might-not-exist\",\"occurredAt\":\"2022-09-22T12:16:04+01:00\",\"additionalInformation\":{\"RSRScore\":45.33,\"RSRBand\":\"H\",\"RSRStaticOrDynamic\":\"STATIC\",\"OSPIndecentScore\":5.79,\"OSPIndecentBand\":\"H\",\"OSPContactScore\":38.7,\"OSPContactBand\":\"V\",\"EventNumber\":1,\"AssessmentDate\":\"2022-09-22T12:16:04+01:00\"},\"personReference\":{\"identifiers\":[{\"type\":\"CRN\",\"value\":\"X552020\"}]}}", + "Message": "{\"eventType\":\"risk-assessment.scores.rsr.determined\",\"version\":1,\"description\":\"Risk assessment scores have been determined\",\"detailUrl\":\"https://some-url-where-we-can-get-more-info-this-might-not-exist\",\"occurredAt\":\"2022-09-22T12:16:04+01:00\",\"additionalInformation\":{\"RSRScore\":45.33,\"RSRBand\":\"H\",\"RSRStaticOrDynamic\":\"STATIC\",\"OSPIndecentScore\":5.79,\"OSPIndecentBand\":\"H\",\"OSPIndirectIndecentScore\":15.32,\"OSPIndirectIndecentBand\":\"V\",\"OSPContactScore\":38.7,\"OSPContactBand\":\"V\",\"OSPDirectContactScore\":21.2,\"OSPDirectContactBand\":\"H\",\"EventNumber\":1,\"AssessmentDate\":\"2022-09-22T12:16:04+01:00\"},\"personReference\":{\"identifiers\":[{\"type\":\"CRN\",\"value\":\"X552020\"}]}}", "Timestamp": "2022-05-04T08:06:46.704Z", "SignatureVersion": "1", "Signature": "", @@ -11,7 +11,7 @@ "MessageAttributes": { "eventType": { "Type": "String", - "Value": "risk-assessment.scores.rsr.determined" + "Value": "risk-assessment.scores.determined" } } } \ No newline at end of file diff --git a/projects/risk-assessment-scores-to-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt b/projects/risk-assessment-scores-to-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt index 582fa91efb..65e06e8991 100644 --- a/projects/risk-assessment-scores-to-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt +++ b/projects/risk-assessment-scores-to-delius/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/IntegrationTest.kt @@ -3,11 +3,7 @@ package uk.gov.justice.digital.hmpps import org.assertj.core.api.Assertions.assertThat import org.hamcrest.MatcherAssert import org.hamcrest.Matchers -import org.junit.jupiter.api.MethodOrderer -import org.junit.jupiter.api.Order -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestMethodOrder -import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.api.* import org.mockito.kotlin.verify import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value @@ -68,6 +64,16 @@ internal class IntegrationTest { verify(telemetryService).trackEvent("RsrScoresUpdated", notification.message.telemetryProperties()) } + @Test + fun `handles null OSP-IIC and OSP-DC bands`() { + val notification = Notification( + message = MessageGenerator.RSR_SCORES_DETERMINED_WITHOUT_OSPIIC_OSPDC, + attributes = MessageAttributes("risk-assessment.scores.determined") + ) + channelManager.getChannel(queueName).publishAndWait(notification) + verify(telemetryService).trackEvent("RsrScoresUpdated", notification.message.telemetryProperties()) + } + @Test @Order(1) fun `successfully add OGRS assessment`() { diff --git a/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/RiskScoreService.kt b/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/RiskScoreService.kt index 3920172b7b..c5dc88b7bc 100644 --- a/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/RiskScoreService.kt +++ b/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/RiskScoreService.kt @@ -6,14 +6,15 @@ import org.springframework.jdbc.core.SqlParameter import org.springframework.jdbc.core.namedparam.MapSqlParameterSource import org.springframework.jdbc.core.simple.SimpleJdbcCall import org.springframework.stereotype.Service +import uk.gov.justice.digital.hmpps.flags.FeatureFlags import uk.gov.justice.digital.hmpps.messaging.RiskAssessment import java.sql.SQLException import java.sql.Types import java.time.ZonedDateTime @Service -class RiskScoreService(jdbcTemplate: JdbcTemplate) { - private val updateRsrScoresProcedure = SimpleJdbcCall(jdbcTemplate) +class RiskScoreService(jdbcTemplate: JdbcTemplate, val featureFlags: FeatureFlags) { + private val updateRsrAndOspScoresProcedure = SimpleJdbcCall(jdbcTemplate) .withCatalogName("pkg_triggersupport") .withProcedureName("procUpdateCAS") .withoutProcedureColumnMetaDataAccess() @@ -26,45 +27,62 @@ class RiskScoreService(jdbcTemplate: JdbcTemplate) { SqlParameter("p_osp_score_i", Types.NUMERIC), SqlParameter("p_osp_score_c", Types.NUMERIC), SqlParameter("p_osp_level_i_code", Types.VARCHAR), - SqlParameter("p_osp_level_c_code", Types.VARCHAR) + SqlParameter("p_osp_level_c_code", Types.VARCHAR), ) - fun updateRsrScores( + private val SimpleJdbcCall.withIndirectIndecentAndDirectContact + get() = declareParameters( + SqlParameter("p_osp_level_iic_code", Types.VARCHAR), + SqlParameter("p_osp_level_dc_code", Types.VARCHAR), + ) + + fun updateRsrAndOspScores( crn: String, eventNumber: Int?, assessmentDate: ZonedDateTime, rsr: RiskAssessment, ospIndecent: RiskAssessment, - ospContact: RiskAssessment + ospIndirectIndecent: RiskAssessment?, + ospContact: RiskAssessment, + ospDirectContact: RiskAssessment?, ) { try { - updateRsrScoresProcedure.execute( - MapSqlParameterSource() - .addValue("p_crn", crn) - .addValue("p_event_number", eventNumber) - .addValue("p_rsr_assessor_date", assessmentDate) - .addValue("p_rsr_score", rsr.score) - .addValue("p_rsr_level_code", rsr.band) - .addValue("p_osp_score_i", ospIndecent.score) - .addValue("p_osp_score_c", ospContact.score) - .addValue("p_osp_level_i_code", ospIndecent.band) - .addValue("p_osp_level_c_code", ospContact.band) - ) + val params = MapSqlParameterSource() + .addValue("p_crn", crn) + .addValue("p_event_number", eventNumber) + .addValue("p_rsr_assessor_date", assessmentDate) + .addValue("p_rsr_score", rsr.score) + .addValue("p_rsr_level_code", rsr.band) + .addValue("p_osp_score_i", ospIndecent.score) + .addValue("p_osp_score_c", ospContact.score) + .addValue("p_osp_level_i_code", ospIndecent.band) + .addValue("p_osp_level_c_code", ospContact.band) + + if (featureFlags.enabled("osp-indirect-indecent-and-direct-contact")) { + updateRsrAndOspScoresProcedure.withIndirectIndecentAndDirectContact.execute( + params + .addValue("p_osp_level_iic_code", ospIndirectIndecent?.band) + .addValue("p_osp_level_dc_code", ospDirectContact?.band) + ) + } else { + updateRsrAndOspScoresProcedure.execute(params) + } } catch (e: UncategorizedSQLException) { - e.sqlException.takeIf { it.isValidationError() } - ?.parsedValidationMessage() - ?.takeIf { it.isDeliusValidationMessage() } + e.sqlException.takeIf { it.isValidationError } + ?.parsedValidationMessage + ?.takeIf { it.isDeliusValidationMessage } ?.let { throw DeliusValidationError(it) } throw e } } - private fun SQLException.isValidationError() = errorCode == 20000 - private fun SQLException.parsedValidationMessage() = message - ?.replace(Regex("\\n.*"), "") // take the first line - ?.replace(Regex("\\[[^]]++]\\s*"), "") // remove anything inside square brackets - ?.removePrefix("ORA-20000: INTERNAL ERROR: An unexpected error in PL/SQL: ERROR : ") // remove Oracle prefix - ?.trim() + private val SQLException.isValidationError get() = errorCode == 20000 + private val SQLException.parsedValidationMessage + get() = message + ?.replace(Regex("\\n.*"), "") // take the first line + ?.replace(Regex("\\[[^]]++]\\s*"), "") // remove anything inside square brackets + ?.removePrefix("ORA-20000: INTERNAL ERROR: An unexpected error in PL/SQL: ERROR : ") // remove Oracle prefix + ?.trim() - private fun String.isDeliusValidationMessage() = DeliusValidationError.isKnownValidationMessage(this) + private val String.isDeliusValidationMessage get() = DeliusValidationError.isKnownValidationMessage(this) } diff --git a/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt b/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt index ae341e2bb4..bb31366f05 100644 --- a/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt +++ b/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/messaging/Handler.kt @@ -24,14 +24,16 @@ class Handler( when (message.eventType) { "risk-assessment.scores.rsr.determined" -> { try { - riskScoreService.updateRsrScores( + riskScoreService.updateRsrAndOspScores( message.personReference.findCrn() ?: throw IllegalArgumentException("Missing CRN in ${message.personReference}"), message.additionalInformation["EventNumber"] as Int?, message.assessmentDate(), message.rsr(), message.ospIndecent(), - message.ospContact() + message.ospIndirectIndecent(), + message.ospContact(), + message.ospDirectContact(), ) telemetryService.trackEvent("RsrScoresUpdated", message.telemetryProperties()) } catch (e: DeliusValidationError) { @@ -95,14 +97,28 @@ fun HmppsDomainEvent.rsr() = RiskAssessment( fun HmppsDomainEvent.ospIndecent() = RiskAssessment( additionalInformation["OSPIndecentScore"] as Double, - additionalInformation["OSPIndecentBand"] as String + additionalInformation["OSPIndecentBand"] as String, ) +fun HmppsDomainEvent.ospIndirectIndecent() = additionalInformation["OSPIndecentIndirectBand"]?.let { + RiskAssessment( + additionalInformation["OSPIndirectIndecentScore"] as Double, + additionalInformation["OSPIndirectIndecentBand"] as String, + ) +} + fun HmppsDomainEvent.ospContact() = RiskAssessment( additionalInformation["OSPContactScore"] as Double, - additionalInformation["OSPContactBand"] as String + additionalInformation["OSPContactBand"] as String, ) +fun HmppsDomainEvent.ospDirectContact() = additionalInformation["OSPDirectContactBand"]?.let { + RiskAssessment( + additionalInformation["OSPDirectContactScore"] as Double, + additionalInformation["OSPDirectContactBand"] as String, + ) +} + fun HmppsDomainEvent.ogrsScore() = OgrsScore( additionalInformation["OGRS3Yr1"] as Int, additionalInformation["OGRS3Yr2"] as Int diff --git a/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/RiskScoreServiceTest.kt b/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/RiskScoreServiceTest.kt index b50052b25b..8b05671233 100644 --- a/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/RiskScoreServiceTest.kt +++ b/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/RiskScoreServiceTest.kt @@ -3,15 +3,14 @@ package uk.gov.justice.digital.hmpps.integrations.delius import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.containsString import org.hamcrest.Matchers.equalTo +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertDoesNotThrow import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith import org.mockito.Mock import org.mockito.MockedConstruction -import org.mockito.Mockito.any -import org.mockito.Mockito.mockConstructionWithAnswer -import org.mockito.Mockito.verify +import org.mockito.Mockito.* import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.check import org.mockito.kotlin.whenever @@ -21,6 +20,7 @@ import org.springframework.jdbc.core.namedparam.MapSqlParameterSource import org.springframework.jdbc.core.namedparam.SqlParameterSource import org.springframework.jdbc.core.simple.SimpleJdbcCall import uk.gov.justice.digital.hmpps.datetime.EuropeLondon +import uk.gov.justice.digital.hmpps.flags.FeatureFlags import uk.gov.justice.digital.hmpps.messaging.RiskAssessment import java.sql.SQLException import java.time.ZonedDateTime @@ -30,23 +30,42 @@ internal class RiskScoreServiceTest { @Mock private lateinit var jdbcTemplate: JdbcTemplate + @Mock + private lateinit var featureFlags: FeatureFlags + @Mock private lateinit var simpleJdbcCall: SimpleJdbcCall + @BeforeEach + fun featureFlag() { + givenTheOspFlagIs(true) + } + @Test fun `scores are passed to the database procedure`() { givenTheDatabaseProcedureSucceeds().use { assertDoesNotThrow { - whenUpdatingRsrScores() + whenUpdatingRsrAndOspScores() thenProcedureIsCalled() } } } + @Test + fun `scores are passed to the old database procedure when flag is disabled`() { + givenTheOspFlagIs(false) + givenTheDatabaseProcedureSucceeds().use { + assertDoesNotThrow { + whenUpdatingRsrAndOspScores() + thenTheOldProcedureIsCalled() + } + } + } + @Test fun `known validation errors are logged to telemetry and wrapped`() { givenTheDatabaseProcedureThrows(sqlException("CRN/Offender does not exist", 20000)).use { - val exception = assertThrows { whenUpdatingRsrScores() } + val exception = assertThrows { whenUpdatingRsrAndOspScores() } assertThat(exception.message, equalTo("CRN/Offender does not exist")) } } @@ -54,7 +73,7 @@ internal class RiskScoreServiceTest { @Test fun `unknown errors are not wrapped`() { givenTheDatabaseProcedureThrows(RuntimeException("unknown error")).use { - val exception = assertThrows { whenUpdatingRsrScores() } + val exception = assertThrows { whenUpdatingRsrAndOspScores() } assertThat(exception.message, equalTo("unknown error")) } } @@ -62,7 +81,7 @@ internal class RiskScoreServiceTest { @Test fun `unknown SQL errors are not wrapped`() { givenTheDatabaseProcedureThrows(sqlException()).use { - val exception = assertThrows { whenUpdatingRsrScores() } + val exception = assertThrows { whenUpdatingRsrAndOspScores() } assertThat(exception.message, containsString("uncategorized SQLException")) } } @@ -70,19 +89,21 @@ internal class RiskScoreServiceTest { @Test fun `unknown validation errors are not wrapped`() { givenTheDatabaseProcedureThrows(sqlException("A validation error we haven't seen before", 20000)).use { - val exception = assertThrows { whenUpdatingRsrScores() } + val exception = assertThrows { whenUpdatingRsrAndOspScores() } assertThat(exception.message, containsString("A validation error we haven't seen before")) } } - private fun whenUpdatingRsrScores() { - RiskScoreService(jdbcTemplate).updateRsrScores( - "A000001", - 123, - ZonedDateTime.of(2022, 12, 15, 9, 0, 0, 0, EuropeLondon), - RiskAssessment(1.00, "A"), - RiskAssessment(2.00, "B"), - RiskAssessment(3.00, "C") + private fun whenUpdatingRsrAndOspScores() { + RiskScoreService(jdbcTemplate, featureFlags).updateRsrAndOspScores( + crn = "A000001", + eventNumber = 123, + assessmentDate = ZonedDateTime.of(2022, 12, 15, 9, 0, 0, 0, EuropeLondon), + rsr = RiskAssessment(1.00, "A"), + ospIndecent = RiskAssessment(2.00, "B"), + ospIndirectIndecent = RiskAssessment(3.00, "C"), + ospContact = RiskAssessment(4.00, "D"), + ospDirectContact = RiskAssessment(5.00, "E"), ) } @@ -94,9 +115,30 @@ internal class RiskScoreServiceTest { "p_rsr_score" to 1.00, "p_rsr_level_code" to "A", "p_osp_score_i" to 2.00, - "p_osp_score_c" to 3.00, + "p_osp_score_c" to 4.00, + "p_osp_level_i_code" to "B", + "p_osp_level_c_code" to "D", + "p_osp_level_iic_code" to "C", + "p_osp_level_dc_code" to "E", + ) + verify(simpleJdbcCall).execute( + check { params -> + assertThat(params.values, equalTo(expectedValues)) + } + ) + } + + private fun thenTheOldProcedureIsCalled() { + val expectedValues = mapOf( + "p_crn" to "A000001", + "p_event_number" to 123, + "p_rsr_assessor_date" to ZonedDateTime.of(2022, 12, 15, 9, 0, 0, 0, EuropeLondon), + "p_rsr_score" to 1.00, + "p_rsr_level_code" to "A", + "p_osp_score_i" to 2.00, + "p_osp_score_c" to 4.00, "p_osp_level_i_code" to "B", - "p_osp_level_c_code" to "C" + "p_osp_level_c_code" to "D" ) verify(simpleJdbcCall).execute( check { params -> @@ -109,6 +151,9 @@ internal class RiskScoreServiceTest { whenever(simpleJdbcCall.withProcedureName("procUpdateCAS")).thenReturn(simpleJdbcCall) whenever(simpleJdbcCall.withoutProcedureColumnMetaDataAccess()).thenReturn(simpleJdbcCall) whenever(simpleJdbcCall.declareParameters(*Array(9) { any() })).thenReturn(simpleJdbcCall) + if (featureFlags.enabled("osp-indirect-indecent-and-direct-contact")) { + whenever(simpleJdbcCall.declareParameters(*Array(2) { any() })).thenReturn(simpleJdbcCall) + } return mockConstructionWithAnswer(SimpleJdbcCall::class.java, { simpleJdbcCall }) } @@ -118,6 +163,10 @@ internal class RiskScoreServiceTest { return mockedConstruction } + private fun givenTheOspFlagIs(value: Boolean) { + whenever(featureFlags.enabled("osp-indirect-indecent-and-direct-contact")).thenReturn(value) + } + private fun sqlException(message: String? = null, code: Int = 20000) = UncategorizedSQLException("error", "sql", SQLException(message, "", code)) } diff --git a/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/HandlerTest.kt b/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/HandlerTest.kt index a791821313..1fc97faf81 100644 --- a/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/HandlerTest.kt +++ b/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/digital/hmpps/messaging/HandlerTest.kt @@ -65,13 +65,15 @@ internal class HandlerTest { handler.handle(message) // Then it is processed - verify(riskScoreService).updateRsrScores( + verify(riskScoreService).updateRsrAndOspScores( message.message.personReference.findCrn()!!, message.message.additionalInformation["EventNumber"] as Int, message.message.assessmentDate(), message.message.rsr(), message.message.ospIndecent(), - message.message.ospContact() + message.message.ospIndirectIndecent(), + message.message.ospContact(), + message.message.ospDirectContact() ) verify(telemetryService).trackEvent("RsrScoresUpdated", message.message.telemetryProperties()) } @@ -105,13 +107,15 @@ internal class HandlerTest { attributes = MessageAttributes("risk-assessment.scores.determined") ) whenever( - riskScoreService.updateRsrScores( + riskScoreService.updateRsrAndOspScores( message.message.personReference.findCrn()!!, message.message.additionalInformation["EventNumber"] as Int, message.message.assessmentDate(), message.message.rsr(), message.message.ospIndecent(), - message.message.ospContact() + message.message.ospIndirectIndecent(), + message.message.ospContact(), + message.message.ospDirectContact() ) ).thenThrow(DeliusValidationError("No Event number provided"))