diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index c58b64d990..a6f1cac705 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -27,6 +27,7 @@ on: - '["pre-sentence-reports-to-delius"]' - '["prison-case-notes-to-probation"]' - '["prison-custody-status-to-delius"]' + - '["risk-assessment-scores-to-delius"]' - '["workforce-allocations-to-delius"]' # GitHub Actions doesn't support dynamic choices, we must add each project here to enable manual deployments # See https://github.com/community/community/discussions/11795 diff --git a/.idea/runConfigurations/risk_assessment_scores_to_delius.xml b/.idea/runConfigurations/risk_assessment_scores_to_delius.xml new file mode 100644 index 0000000000..0b42735976 --- /dev/null +++ b/.idea/runConfigurations/risk_assessment_scores_to_delius.xml @@ -0,0 +1,12 @@ + + + + \ No newline at end of file diff --git a/libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/datetime/ZonedDateTimeDeserializer.kt b/libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/datetime/ZonedDateTimeDeserializer.kt index 446d71f505..c089b7ff60 100644 --- a/libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/datetime/ZonedDateTimeDeserializer.kt +++ b/libs/commons/src/main/kotlin/uk/gov/justice/digital/hmpps/datetime/ZonedDateTimeDeserializer.kt @@ -28,12 +28,14 @@ class ZonedDateTimeDeserializer : JsonDeserializer() { .optionalEnd() .optionalEnd() .toFormatter() + + fun deserialize(text: String): ZonedDateTime { + val datetime = formatter.parseBest(text, ZonedDateTime::from, LocalDateTime::from) + return if (datetime is ZonedDateTime) datetime.withZoneSameInstant(EuropeLondon) + else (datetime as LocalDateTime).atZone(EuropeLondon) + } } @Throws(IOException::class, JsonProcessingException::class) - override fun deserialize(parser: JsonParser, context: DeserializationContext?): ZonedDateTime { - val datetime = formatter.parseBest(parser.text, ZonedDateTime::from, LocalDateTime::from) - return if (datetime is ZonedDateTime) datetime.withZoneSameInstant(EuropeLondon) - else (datetime as LocalDateTime).atZone(EuropeLondon) - } + override fun deserialize(parser: JsonParser, context: DeserializationContext?) = deserialize(parser.text) } diff --git a/projects/risk-assessment-scores-to-delius/README.md b/projects/risk-assessment-scores-to-delius/README.md index fb3245ae6e..4c2d5af163 100644 --- a/projects/risk-assessment-scores-to-delius/README.md +++ b/projects/risk-assessment-scores-to-delius/README.md @@ -10,28 +10,27 @@ Sample message: ```json { - "eventType": "risk-assessment.scores.determined", + "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", - "occurredAt": "2022-12-04T10:42:43+00:00", + "detailUrl": "https://some-url-where-we-can-get-more-info-this-might-not-exist", + "occurredAt": "2022-09-22T12:16:04+01:00", "additionalInformation": { - "RSRScore": 0, - "RSRBand": 0, - "OSPIndecentScore": 0, - "OSPIndecentBand": 0, - "OSPContactScore": 0, - "OSPContactBand": 0, - "EventNumber": 0, - "OGRS3Yr1": 0, - "OGRS3Yr2": 0, - "AssessmentDate": "2022-12-04T10:42:43+00:00" + "RSRScore": 45.33, + "RSRBand": "H", + "RSRStaticOrDynamic": "STATIC", + "OSPIndecentScore": 5.79, + "OSPIndecentBand": "H", + "OSPContactScore": 38.70, + "OSPContactBand": "V", + "EventNumber": 1, + "AssessmentDate": "2022-09-22T12:16:04+01:00" }, "personReference": { "identifiers": [ { "type": "CRN", - "value": "X08769" + "value": "X552020" } ] } diff --git a/projects/risk-assessment-scores-to-delius/build.gradle.kts b/projects/risk-assessment-scores-to-delius/build.gradle.kts index 759b71eb60..39fdb91ae3 100644 --- a/projects/risk-assessment-scores-to-delius/build.gradle.kts +++ b/projects/risk-assessment-scores-to-delius/build.gradle.kts @@ -7,9 +7,8 @@ dependencies { implementation("org.springframework:spring-jms") implementation("org.springframework.boot:spring-boot-starter-actuator") - implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-jdbc") implementation("org.springframework.boot:spring-boot-starter-security") - implementation("org.springframework.boot:spring-boot-starter-validation") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") diff --git a/projects/risk-assessment-scores-to-delius/deploy/values-dev.yml b/projects/risk-assessment-scores-to-delius/deploy/values-dev.yml index 28efed2dcf..abaa22b87d 100644 --- a/projects/risk-assessment-scores-to-delius/deploy/values-dev.yml +++ b/projects/risk-assessment-scores-to-delius/deploy/values-dev.yml @@ -1,7 +1,5 @@ environment_name: delius-test -enabled: false # TODO set this to true when you're ready to deploy your service - env: SENTRY_ENVIRONMENT: dev LOGGING_LEVEL_UK_GOV_DIGITAL_JUSTICE_HMPPS: DEBUG \ No newline at end of file diff --git a/projects/risk-assessment-scores-to-delius/deploy/values-preprod.yml b/projects/risk-assessment-scores-to-delius/deploy/values-preprod.yml index 8381753a79..da011e3ed1 100644 --- a/projects/risk-assessment-scores-to-delius/deploy/values-preprod.yml +++ b/projects/risk-assessment-scores-to-delius/deploy/values-preprod.yml @@ -1,6 +1,4 @@ environment_name: delius-pre-prod -enabled: false # TODO set this to true when you're ready to deploy your service - env: SENTRY_ENVIRONMENT: preprod diff --git a/projects/risk-assessment-scores-to-delius/deploy/values-prod.yml b/projects/risk-assessment-scores-to-delius/deploy/values-prod.yml index 3264163e4e..ebed96aeed 100644 --- a/projects/risk-assessment-scores-to-delius/deploy/values-prod.yml +++ b/projects/risk-assessment-scores-to-delius/deploy/values-prod.yml @@ -1,6 +1,4 @@ environment_name: delius-prod -enabled: false # TODO set this to true when you're ready to deploy your service - env: SENTRY_ENVIRONMENT: prod diff --git a/projects/risk-assessment-scores-to-delius/deploy/values.yml b/projects/risk-assessment-scores-to-delius/deploy/values.yml index 7cd7f7756e..1b06605c80 100644 --- a/projects/risk-assessment-scores-to-delius/deploy/values.yml +++ b/projects/risk-assessment-scores-to-delius/deploy/values.yml @@ -8,5 +8,5 @@ secrets: SPRING_DATASOURCE_URL: /delius-database/jdbc-url SPRING_DATASOURCE_USERNAME: /risk-assessment-scores-to-delius/db-username SPRING_DATASOURCE_PASSWORD: /risk-assessment-scores-to-delius/db-password - SPRING_JMS_TEMPLATE_DEFAULT-DESTINATION: /risk-assessment-scores-to-delius/queue-name + SPRING_JMS_TEMPLATE_DEFAULT-DESTINATION: /risk-assessment-scores-to-delius/sqs-queue-name SENTRY_DSN: /risk-assessment-scores-to-delius/sentry-dsn 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 new file mode 100644 index 0000000000..ac8d361219 --- /dev/null +++ b/projects/risk-assessment-scores-to-delius/src/dev/kotlin/uk/gov/justice/digital/hmpps/MessageGenerator.kt @@ -0,0 +1,9 @@ +package uk.gov.justice.digital.hmpps + +import uk.gov.justice.digital.hmpps.message.HmppsEvent +import uk.gov.justice.digital.hmpps.resourceloader.ResourceLoader + +object MessageGenerator { + val RSR_SCORES_DETERMINED = ResourceLoader.message("rsr-scores-determined") + val OGRS_SCORES_DETERMINED = ResourceLoader.message("ogrs-scores-determined") +} 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 new file mode 100644 index 0000000000..3737d0b2a3 --- /dev/null +++ b/projects/risk-assessment-scores-to-delius/src/dev/resources/db/h2.sql @@ -0,0 +1,4 @@ +drop schema if exists pkg_triggersupport cascade; + +create schema pkg_triggersupport; +create alias pkg_triggersupport.procRsrUpdateCas as 'void stub() {}'; 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 new file mode 100644 index 0000000000..ba3f6308dc --- /dev/null +++ b/projects/risk-assessment-scores-to-delius/src/dev/resources/db/oracle.sql @@ -0,0 +1,38 @@ +-- 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 procRsrUpdateCas( + p_crn in varchar2, + p_event_number in int, + p_rsr_assessor_date in timestamp, + p_rsr_score in float, + p_rsr_band in varchar2, + p_osp_indecent_score in float, + p_osp_indecent_band in varchar2, + p_osp_contact_score in float, + p_osp_contact_band in varchar2, + p_error out varchar2 + ); +end pkg_triggersupport; +grant execute on pkg_triggersupport to delius_app_schema; + +create or replace package body pkg_triggersupport as + procedure procRsrUpdateCas( + p_crn in varchar2, + p_event_number in int, + p_rsr_assessor_date in timestamp, + p_rsr_score in float, + p_rsr_band in varchar2, + p_osp_indecent_score in float, + p_osp_indecent_band in varchar2, + p_osp_contact_score in float, + p_osp_contact_band in varchar2, + p_error out varchar2 + ) is + begin + -- For testing: + if length(p_crn) != 7 then p_error := 'Invalid CRN'; end if; + -- ... + end procRsrUpdateCas; +end pkg_triggersupport; +/ diff --git a/projects/risk-assessment-scores-to-delius/src/dev/resources/messages/ogrs-scores-determined.json b/projects/risk-assessment-scores-to-delius/src/dev/resources/messages/ogrs-scores-determined.json new file mode 100644 index 0000000000..95d52577aa --- /dev/null +++ b/projects/risk-assessment-scores-to-delius/src/dev/resources/messages/ogrs-scores-determined.json @@ -0,0 +1,17 @@ +{ + "Type": "Notification", + "MessageId": "", + "TopicArn": "", + "Message": "{\"eventType\":\"risk-assessment.scores.ogrs.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-15T15:34:48+01:00\",\"additionalInformation\":{\"OGRS3Yr1\":4,\"OGRS3Yr2\":8,\"EventNumber\":1,\"AssessmentDate\":\"2022-09-15T15:34:48+01:00\"},\"personReference\":{\"identifiers\":[{\"type\":\"CRN\",\"value\":\"X552020\"}]}}", + "Timestamp": "2022-09-15T14:34:48.123Z", + "SignatureVersion": "1", + "Signature": "", + "SigningCertURL": "", + "UnsubscribeURL": "", + "MessageAttributes": { + "eventType": { + "Type": "String", + "Value": "risk-assessment.scores.ogrs.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 new file mode 100644 index 0000000000..a848ec3a3f --- /dev/null +++ b/projects/risk-assessment-scores-to-delius/src/dev/resources/messages/rsr-scores-determined.json @@ -0,0 +1,17 @@ +{ + "Type": "Notification", + "MessageId": "", + "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.rsr.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 02938c0d52..597074c654 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 @@ -1,12 +1,33 @@ package uk.gov.justice.digital.hmpps import org.junit.jupiter.api.Test +import org.mockito.kotlin.verify +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.beans.factory.annotation.Value import org.springframework.boot.test.context.SpringBootTest +import org.springframework.boot.test.mock.mockito.MockBean +import org.springframework.jms.core.JmsTemplate import org.springframework.test.context.ActiveProfiles +import uk.gov.justice.digital.hmpps.jms.convertSendAndWait +import uk.gov.justice.digital.hmpps.listener.telemetryProperties +import uk.gov.justice.digital.hmpps.telemetry.TelemetryService @SpringBootTest @ActiveProfiles("integration-test") internal class IntegrationTest { + @Value("\${spring.jms.template.default-destination}") + private lateinit var queueName: String + + @Autowired + private lateinit var jmsTemplate: JmsTemplate + + @MockBean + private lateinit var telemetryService: TelemetryService + @Test - fun `the context loads`() {} + fun `successfully update RSR scores`() { + val message = MessageGenerator.RSR_SCORES_DETERMINED + jmsTemplate.convertSendAndWait(queueName, message) + verify(telemetryService).trackEvent("RsrScoresUpdated", message.telemetryProperties()) + } } diff --git a/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/exception/FailureToUpdateRiskScores.kt b/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/exception/FailureToUpdateRiskScores.kt new file mode 100644 index 0000000000..35843039d2 --- /dev/null +++ b/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/exception/FailureToUpdateRiskScores.kt @@ -0,0 +1,3 @@ +package uk.gov.justice.digital.hmpps.exception + +class FailureToUpdateRiskScores(override val message: String) : RuntimeException(message) 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 new file mode 100644 index 0000000000..b4afbc4c85 --- /dev/null +++ b/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/integrations/delius/RiskScoreService.kt @@ -0,0 +1,41 @@ +package uk.gov.justice.digital.hmpps.integrations.delius + +import org.springframework.jdbc.core.JdbcTemplate +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.exception.FailureToUpdateRiskScores +import java.time.ZonedDateTime + +@Service +class RiskScoreService(jdbcTemplate: JdbcTemplate) { + private val updateRsrScoresProcedure = SimpleJdbcCall(jdbcTemplate) + .withCatalogName("pkg_triggersupport") + .withProcedureName("procRsrUpdateCas") + + fun updateRsrScores( + crn: String, + eventNumber: Int, + assessmentDate: ZonedDateTime, + rsrScore: Double, + rsrBand: String, + ospIndecentScore: Double, + ospIndecentBand: String, + ospContactScore: Double, + ospContactBand: String, + ) { + val result = updateRsrScoresProcedure.execute( + MapSqlParameterSource() + .addValue("p_crn", crn) + .addValue("p_event_number", eventNumber) + .addValue("p_rsr_assessor_date", assessmentDate) + .addValue("p_rsr_score", rsrScore) + .addValue("p_rsr_band", rsrBand) + .addValue("p_osp_indecent_score", ospIndecentScore) + .addValue("p_osp_indecent_band", ospIndecentBand) + .addValue("p_osp_contact_score", ospContactScore) + .addValue("p_osp_contact_band", ospContactBand) + ) + result["P_ERROR"]?.let { throw FailureToUpdateRiskScores(it as String) } + } +} diff --git a/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/listener/MessageListener.kt b/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/listener/MessageListener.kt index 02a4fb9764..6e61c3a674 100644 --- a/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/listener/MessageListener.kt +++ b/projects/risk-assessment-scores-to-delius/src/main/kotlin/uk/gov/justice/digital/hmpps/listener/MessageListener.kt @@ -5,13 +5,17 @@ import org.slf4j.LoggerFactory import org.springframework.jms.annotation.EnableJms import org.springframework.jms.annotation.JmsListener import org.springframework.stereotype.Component +import uk.gov.justice.digital.hmpps.datetime.ZonedDateTimeDeserializer +import uk.gov.justice.digital.hmpps.integrations.delius.RiskScoreService import uk.gov.justice.digital.hmpps.message.HmppsEvent import uk.gov.justice.digital.hmpps.telemetry.TelemetryService @Component @EnableJms -class MessageListener(private val telemetryService: TelemetryService) { - +class MessageListener( + private val telemetryService: TelemetryService, + private val riskScoreService: RiskScoreService, +) { companion object { val log: Logger = LoggerFactory.getLogger(this::class.java) } @@ -20,6 +24,32 @@ class MessageListener(private val telemetryService: TelemetryService) { fun receive(hmppsEvent: HmppsEvent) { log.debug("received $hmppsEvent") telemetryService.hmppsEventReceived(hmppsEvent) - TODO("Not yet implemented") + when (hmppsEvent.eventType) { + "risk-assessment.scores.rsr.determined" -> { + riskScoreService.updateRsrScores( + hmppsEvent.personReference.findCrn() ?: throw IllegalArgumentException("Missing CRN in ${hmppsEvent.personReference}"), + hmppsEvent.additionalInformation["EventNumber"] as Int, + (hmppsEvent.additionalInformation["AssessmentDate"] as String).toDate(), + hmppsEvent.additionalInformation["RSRScore"] as Double, + hmppsEvent.additionalInformation["RSRBand"] as String, + hmppsEvent.additionalInformation["OSPIndecentScore"] as Double, + hmppsEvent.additionalInformation["OSPIndecentBand"] as String, + hmppsEvent.additionalInformation["OSPContactScore"] as Double, + hmppsEvent.additionalInformation["OSPContactBand"] as String, + ) + telemetryService.trackEvent("RsrScoresUpdated", hmppsEvent.telemetryProperties()) + } + "risk-assessment.scores.ogrs.determined" -> + telemetryService.trackEvent("UnsupportedEventType", hmppsEvent.telemetryProperties()) + else -> throw IllegalArgumentException("Unexpected event type ${hmppsEvent.eventType}") + } } } + +fun HmppsEvent.telemetryProperties() = mapOf( + "occurredAt" to occurredAt.toString(), + "personReference" to personReference.toString(), + "additionalInformation" to additionalInformation.toString(), +) + +fun String.toDate() = ZonedDateTimeDeserializer.deserialize(this) diff --git a/projects/risk-assessment-scores-to-delius/src/main/resources/application.yml b/projects/risk-assessment-scores-to-delius/src/main/resources/application.yml index 8bf5e7c3b4..c95f45676e 100644 --- a/projects/risk-assessment-scores-to-delius/src/main/resources/application.yml +++ b/projects/risk-assessment-scores-to-delius/src/main/resources/application.yml @@ -1,16 +1,10 @@ # Default config -spring: - jpa: - database-platform: org.hibernate.dialect.Oracle12cDialect - hibernate.ddl-auto: validate - jms: - template.default-destination: message-queue - listener: - concurrency: 1 - max-concurrency: 10 - acknowledge-mode: client - -delius.db.username: RiskAssessmentScoresToDelius # Should match value in [deploy/database/access.yml]. +spring.jms: + template.default-destination: message-queue + listener: + concurrency: 1 + max-concurrency: 10 + acknowledge-mode: client management.endpoints.web: base-path: / @@ -24,7 +18,8 @@ spring.config.activate.on-profile: [ "dev", "integration-test", "oracle" ] spring: activemq.broker-url: vm://localhost?broker.persistent=false&broker.useShutdownHook=false datasource.url: jdbc:h2:file:./dev;MODE=Oracle;DEFAULT_NULL_ORDERING=HIGH;AUTO_SERVER=true;AUTO_SERVER_PORT=9092 - jpa.hibernate.ddl-auto: create-drop + sql.init.mode: always + sql.init.data-locations: classpath:db/h2.sql management.endpoints.web: base-path: / @@ -42,12 +37,8 @@ spring.datasource.url: jdbc:h2:file:./test;MODE=Oracle;DEFAULT_NULL_ORDERING=HIG --- spring.config.activate.on-profile: oracle spring: + sql.init.mode: never datasource: url: 'jdbc:oracle:thin:@//localhost:1521/XEPDB1' - username: delius_pool + username: delius_app_schema password: NDelius1 - jpa: - properties: - hibernate.dialect: org.hibernate.dialect.Oracle12cDialect - hibernate.ddl-auto: validate -delius.db.username: NationalUser \ No newline at end of file diff --git a/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/hmpps/listener/MessageListenerTest.kt b/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/hmpps/listener/MessageListenerTest.kt index 9a70e9dc35..d6dae50373 100644 --- a/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/hmpps/listener/MessageListenerTest.kt +++ b/projects/risk-assessment-scores-to-delius/src/test/kotlin/uk/gov/justice/hmpps/listener/MessageListenerTest.kt @@ -1,30 +1,86 @@ package uk.gov.justice.hmpps.listener import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows import org.junit.jupiter.api.extension.ExtendWith import org.mockito.InjectMocks import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.verify +import org.mockito.kotlin.verifyNoInteractions +import uk.gov.justice.digital.hmpps.MessageGenerator +import uk.gov.justice.digital.hmpps.integrations.delius.RiskScoreService import uk.gov.justice.digital.hmpps.listener.MessageListener -import uk.gov.justice.digital.hmpps.message.HmppsEvent +import uk.gov.justice.digital.hmpps.listener.telemetryProperties +import uk.gov.justice.digital.hmpps.listener.toDate +import uk.gov.justice.digital.hmpps.message.PersonReference import uk.gov.justice.digital.hmpps.telemetry.TelemetryService -import java.time.ZonedDateTime @ExtendWith(MockitoExtension::class) internal class MessageListenerTest { @Mock lateinit var telemetryService: TelemetryService + @Mock lateinit var riskScoreService: RiskScoreService @InjectMocks lateinit var messageListener: MessageListener @Test - fun messageIsLoggedToTelemetry() { + fun `message is logged to telemetry`() { // Given an event - val event = HmppsEvent("test.event.type", 1, "https//detail/url", ZonedDateTime.now()) + val event = MessageGenerator.RSR_SCORES_DETERMINED // When it is received - try { messageListener.receive(event) } catch (_: Throwable) { } + messageListener.receive(event) // Then it is logged to telemetry verify(telemetryService).hmppsEventReceived(event) } + + @Test + fun `RSR messages are processed`() { + // Given an RSR message + val event = MessageGenerator.RSR_SCORES_DETERMINED + + // When it is received + messageListener.receive(event) + + // Then it is processed + verify(riskScoreService).updateRsrScores( + event.personReference.findCrn()!!, + event.additionalInformation["EventNumber"] as Int, + (event.additionalInformation["AssessmentDate"] as String).toDate(), + event.additionalInformation["RSRScore"] as Double, + event.additionalInformation["RSRBand"] as String, + event.additionalInformation["OSPIndecentScore"] as Double, + event.additionalInformation["OSPIndecentBand"] as String, + event.additionalInformation["OSPContactScore"] as Double, + event.additionalInformation["OSPContactBand"] as String, + ) + verify(telemetryService).trackEvent("RsrScoresUpdated", event.telemetryProperties()) + } + + @Test + fun `OGRS messages are ignored`() { + // Given an OGRS message + val event = MessageGenerator.OGRS_SCORES_DETERMINED + + // When it is received + messageListener.receive(event) + + // Then it is not processed + verifyNoInteractions(riskScoreService) + verify(telemetryService).trackEvent("UnsupportedEventType", event.telemetryProperties()) + } + + @Test + fun `unknown messages are thrown`() { + assertThrows { + messageListener.receive(MessageGenerator.RSR_SCORES_DETERMINED.copy(eventType = "INVALID")) + } + } + + @Test + fun `missing CRN is thrown`() { + assertThrows { + messageListener.receive(MessageGenerator.RSR_SCORES_DETERMINED.copy(personReference = PersonReference())) + } + } }