Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
anthony-britton-moj committed Oct 17, 2023
1 parent 7480270 commit 7fb7714
Show file tree
Hide file tree
Showing 8 changed files with 259 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
import uk.gov.justice.digital.hmpps.data.generator.SentenceGenerator
import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.ContactRepository
import uk.gov.justice.digital.hmpps.integrations.delius.contact.entity.ContactType
import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.PersonManagerRepository
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping.Companion.BESPOKE_CATEGORY_CODE
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping.Companion.STANDARD_CATEGORY_CODE
Expand All @@ -20,6 +22,8 @@ import uk.gov.justice.digital.hmpps.messaging.HmppsChannelManager
import uk.gov.justice.digital.hmpps.resourceloader.ResourceLoader
import uk.gov.justice.digital.hmpps.service.ActionResult
import uk.gov.justice.digital.hmpps.telemetry.TelemetryService
import uk.gov.justice.digital.hmpps.test.CustomMatchers.isCloseTo
import java.time.ZonedDateTime

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class LicenceActivatedIntegrationTest {
Expand All @@ -44,6 +48,9 @@ class LicenceActivatedIntegrationTest {
@Autowired
lateinit var pmr: PersonManagerRepository

@Autowired
lateinit var contactRepository: ContactRepository

@Test
fun `add licence conditions`() {
val sentence = SentenceGenerator.SENTENCE_CREATE_LC
Expand All @@ -70,6 +77,7 @@ class LicenceActivatedIntegrationTest {
ActionResult.Type.AdditionalLicenceConditionsAdded.name,
telemetryProperties
)
verify(telemetryService).trackEvent(ActionResult.Type.BespokeLicenceConditionAdded.name, telemetryProperties)

val conditions = lcr.findByDisposalId(sentence.id)
assertThat(conditions.size, equalTo(4))
Expand Down Expand Up @@ -110,5 +118,24 @@ class LicenceActivatedIntegrationTest {
additional.map { it.notes },
containsInAnyOrder("Additional Licence Condition One", "Additional Licence Condition Two")
)

val lpop = contactRepository.findAll().filter { it.personId == person.id && it.type.code == ContactType.LPOP }
assertThat(lpop.size, equalTo(1))
assertThat(
lpop.first().notes,
equalTo(
"""
|Delius has been updated with licence conditions entered in the Create and Vary a licence service.
|Select the following link to navigate to Create and Vary a licence service and view the licence.
""".trimMargin()
)
)
val occurredAt = ZonedDateTime.parse("2022-12-04T10:42:43+00:00")
assertThat(lpop.first().date, equalTo(occurredAt.toLocalDate()))
assertThat(lpop.first().startTime!!, isCloseTo(occurredAt))

// send again to confirm licence conditions are not duplicated
channelManager.getChannel(queueName).publishAndWait(notification)
verify(telemetryService).trackEvent(ActionResult.Type.NoChangeToLicenceConditions.name, telemetryProperties)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.hibernate.annotations.Immutable
import org.hibernate.annotations.Where
import org.springframework.data.jpa.repository.EntityGraph
import org.springframework.data.jpa.repository.JpaRepository
import uk.gov.justice.digital.hmpps.exception.NotFoundException
import uk.gov.justice.digital.hmpps.integrations.delius.person.entity.Person
import uk.gov.justice.digital.hmpps.integrations.delius.provider.entity.Provider
import uk.gov.justice.digital.hmpps.integrations.delius.provider.entity.Staff
Expand Down Expand Up @@ -52,3 +53,6 @@ interface PersonManagerRepository : JpaRepository<PersonManager, Long> {
@EntityGraph(attributePaths = ["person", "provider", "team", "staff.user"])
fun findByPersonCrn(crn: String): PersonManager?
}

fun PersonManagerRepository.getByCrn(crn: String) =
findByPersonCrn(crn) ?: throw NotFoundException("Person", "crn", crn)
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,25 @@ class CvlHandler(
) : NotificationHandler<HmppsDomainEvent> {

override fun handle(notification: Notification<HmppsDomainEvent>) {
val results = when (notification.eventType?.let { DomainEventType.of(it) }) {
is DomainEventType.LicenceActivated -> licenceActivatedHandler.licenceActivated(notification.message)
else -> listOf(
ActionResult.Ignored("Unexpected Event Type"),
mapOf("eventType" to (notification.eventType ?: ""))
)
}
val eventType = (notification.eventType ?: notification.message.eventType).let { DomainEventType.of(it) }
val results =
when (eventType) {
is DomainEventType.LicenceActivated -> licenceActivatedHandler.licenceActivated(notification.message)
else -> listOf(
ActionResult.Ignored(
"UnexpectedEventType",
mapOf("eventType" to eventType.name)
)
)
}

val failure = results.firstOrNull { it is ActionResult.Failure } as ActionResult.Failure?
if (failure == null) {
results.forEach {
when (it) {
is ActionResult.Success -> telemetryService.trackEvent(it.type.name, it.properties)
is ActionResult.Ignored -> telemetryService.trackEvent(it.reason, it.properties)
else -> throw IllegalArgumentException("Unexpected Action Result: $it")
if (it is ActionResult.Success) {
telemetryService.trackEvent(it.type.name, it.properties)
} else if (it is ActionResult.Ignored) {
telemetryService.trackEvent(it.reason, it.properties)
}
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ sealed interface DomainEventType {
private val types = listOf(LicenceActivated)
.associateBy { it.name }

fun of(name: String) = types[name] ?: Other(name)
fun of(name: String): DomainEventType = types[name] ?: Other(name)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ package uk.gov.justice.digital.hmpps.service

import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import uk.gov.justice.digital.hmpps.exception.NotFoundException
import uk.gov.justice.digital.hmpps.integrations.cvl.ActivatedLicence
import uk.gov.justice.digital.hmpps.integrations.cvl.Describable
import uk.gov.justice.digital.hmpps.integrations.cvl.telemetryProperties
import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.PersonManager
import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.PersonManagerRepository
import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.getByCrn
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping.Companion.BESPOKE_CATEGORY_CODE
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping.Companion.BESPOKE_SUB_CATEGORY_CODE
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMapping.Companion.STANDARD_CATEGORY_CODE
Expand Down Expand Up @@ -41,15 +41,16 @@ class LicenceConditionApplier(
activatedLicence: ActivatedLicence,
occurredAt: ZonedDateTime
): List<ActionResult> {
val com = personManagerRepository.findByPersonCrn(crn) ?: throw NotFoundException("Person", "crn", crn)
return disposalRepository.findCustodialSentences(crn)
.flatMap {
applyLicenceConditions(
SentencedCase(com, it, licenceConditionService.findByDisposalId(it.id)),
activatedLicence,
occurredAt
)
}
val com = personManagerRepository.getByCrn(crn)
return disposalRepository.findCustodialSentences(crn).ifEmpty {
throw IllegalStateException("No Custodial Sentences to apply Licence Conditions")
}.flatMap {
applyLicenceConditions(
SentencedCase(com, it, licenceConditionService.findByDisposalId(it.id)),
activatedLicence,
occurredAt
)
}
}

private fun applyLicenceConditions(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package uk.gov.justice.digital.hmpps.service

import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
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.any
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import uk.gov.justice.digital.hmpps.converter.NotificationConverter
import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent
import uk.gov.justice.digital.hmpps.message.MessageAttributes
import uk.gov.justice.digital.hmpps.message.Notification
import uk.gov.justice.digital.hmpps.telemetry.TelemetryService

@ExtendWith(MockitoExtension::class)
internal class CvlHandlerTest {
@Mock
internal lateinit var converter: NotificationConverter<HmppsDomainEvent>

@Mock
internal lateinit var telemetryService: TelemetryService

@Mock
internal lateinit var licenceActivatedHandler: LicenceActivatedHandler

@InjectMocks
internal lateinit var handler: CvlHandler

@Test
fun `unexpected event types are ignored`() {
val notification = Notification(
HmppsDomainEvent("unknown.type.event-type", 1),
MessageAttributes("unknown.type.event-type")
)

handler.handle(notification)

verify(telemetryService).trackEvent(
"UnexpectedEventType",
mapOf("eventType" to "unknown.type.event-type"),
mapOf()
)
}

@Test
fun `action results with failure throw exception`() {
val notification = Notification(
HmppsDomainEvent(DomainEventType.LicenceActivated.name, 1),
MessageAttributes(DomainEventType.LicenceActivated.name)
)

whenever(licenceActivatedHandler.licenceActivated(any()))
.thenReturn(listOf(ActionResult.Failure(RuntimeException("Unknown Exception Happened"))))

val ex = assertThrows<RuntimeException> {
handler.handle(notification)
}
assertThat(ex.message, equalTo("Unknown Exception Happened"))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package uk.gov.justice.digital.hmpps.service

import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.instanceOf
import org.junit.jupiter.api.Test
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.any
import org.mockito.kotlin.whenever
import uk.gov.justice.digital.hmpps.exception.NotFoundException
import uk.gov.justice.digital.hmpps.integrations.cvl.CvlClient
import uk.gov.justice.digital.hmpps.message.HmppsDomainEvent
import uk.gov.justice.digital.hmpps.message.PersonIdentifier
import uk.gov.justice.digital.hmpps.message.PersonReference

@ExtendWith(MockitoExtension::class)
internal class LicenceActivatedHandlerTest {
@Mock
internal lateinit var cvlClient: CvlClient

@Mock
internal lateinit var lca: LicenceConditionApplier

@InjectMocks
internal lateinit var lah: LicenceActivatedHandler

@Test
fun `exception returned when crn not found in message`() {
val event = HmppsDomainEvent(DomainEventType.LicenceActivated.name, 1)
val res = lah.licenceActivated(event).first()
assertThat(res, instanceOf(ActionResult.Failure::class.java))
val fail = res as ActionResult.Failure
assertThat(fail.exception, instanceOf(IllegalArgumentException::class.java))
assertThat(fail.exception.message, equalTo("No CRN Provided"))
}

@Test
fun `exception returned when detail url not found in message`() {
val event = HmppsDomainEvent(
DomainEventType.LicenceActivated.name,
1,
personReference = PersonReference(listOf(PersonIdentifier("CRN", "X123456")))
)
val res = lah.licenceActivated(event).first()
assertThat(res, instanceOf(ActionResult.Failure::class.java))
val fail = res as ActionResult.Failure
assertThat(fail.exception, instanceOf(IllegalArgumentException::class.java))
assertThat(fail.exception.message, equalTo("No Detail Url Provided"))
}

@Test
fun `exception returned when activated licence not found`() {
val event = HmppsDomainEvent(
DomainEventType.LicenceActivated.name,
1,
detailUrl = "https://cvl.service.co.uk/licence-activated/58eb2a20-6b0e-416b-b91d-5b98b0c1be7f",
personReference = PersonReference(listOf(PersonIdentifier("CRN", "X123456")))
)
whenever(cvlClient.getActivatedLicence(any())).thenReturn(null)

val res = lah.licenceActivated(event).first()
assertThat(res, instanceOf(ActionResult.Failure::class.java))
val fail = res as ActionResult.Failure
assertThat(fail.exception, instanceOf(NotFoundException::class.java))
assertThat(
fail.exception.message,
equalTo("Activated Licence with detailUrl of https://cvl.service.co.uk/licence-activated/58eb2a20-6b0e-416b-b91d-5b98b0c1be7f not found")
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package uk.gov.justice.digital.hmpps.service

import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
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.whenever
import uk.gov.justice.digital.hmpps.data.generator.PersonGenerator
import uk.gov.justice.digital.hmpps.integrations.cvl.ActivatedLicence
import uk.gov.justice.digital.hmpps.integrations.delius.manager.entity.PersonManagerRepository
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.CvlMappingRepository
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.DisposalRepository
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.LicenceConditionCategoryRepository
import uk.gov.justice.digital.hmpps.integrations.delius.sentence.entity.ReferenceDataRepository
import java.time.LocalDate
import java.time.ZonedDateTime

@ExtendWith(MockitoExtension::class)
internal class LicenceConditionApplierTest {
@Mock
internal lateinit var disposalRepository: DisposalRepository

@Mock
internal lateinit var personManagerRepository: PersonManagerRepository

@Mock
internal lateinit var cvlMappingRepository: CvlMappingRepository

@Mock
internal lateinit var licenceConditionCategoryRepository: LicenceConditionCategoryRepository

@Mock
internal lateinit var referenceDataRepository: ReferenceDataRepository

@Mock
internal lateinit var licenceConditionService: LicenceConditionService

@Mock
internal lateinit var contactService: ContactService

@InjectMocks
internal lateinit var licenceConditionApplier: LicenceConditionApplier

@Test
fun `when no active custodial sentence an error is thrown`() {
val crn = "N678461"
whenever(personManagerRepository.findByPersonCrn(crn)).thenReturn(PersonGenerator.DEFAULT_CM)
whenever(disposalRepository.findCustodialSentences(crn)).thenReturn(listOf())

val ex = assertThrows<IllegalStateException> {
licenceConditionApplier.applyLicenceConditions(
crn,
ActivatedLicence(crn, LocalDate.now(), null, null, listOf(), listOf(), listOf()),
ZonedDateTime.now()
)
}
assertThat(ex.message, equalTo("No Custodial Sentences to apply Licence Conditions"))
}
}

0 comments on commit 7fb7714

Please sign in to comment.