Skip to content

Commit

Permalink
CDPS-1086: Adding tests
Browse files Browse the repository at this point in the history
  • Loading branch information
brightonsbox committed Jan 17, 2025
1 parent c6a4d19 commit e5f8dd1
Show file tree
Hide file tree
Showing 47 changed files with 2,957 additions and 110 deletions.
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ dependencies {
testImplementation("uk.gov.justice.service.hmpps:hmpps-kotlin-spring-boot-starter-test:1.1.1")
testImplementation("org.testcontainers:junit-jupiter:1.20.4")
testImplementation("org.testcontainers:postgresql:1.20.4")
testImplementation("io.mockk:mockk:1.13.13")
testImplementation("org.wiremock:wiremock-standalone:3.10.0")
testImplementation("io.swagger.parser.v3:swagger-parser:2.1.24") {
exclude(group = "io.swagger.core.v3")
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package uk.gov.justice.digital.hmpps.healthandmedication.config

import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.jwt.SignedJWT
import io.opentelemetry.api.trace.Span
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.slf4j.LoggerFactory
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression
import org.springframework.context.annotation.Configuration
import org.springframework.http.HttpHeaders
import org.springframework.web.servlet.HandlerInterceptor
import org.springframework.web.servlet.config.annotation.InterceptorRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
import java.text.ParseException

@Configuration
@ConditionalOnExpression("T(org.apache.commons.lang3.StringUtils).isNotBlank('\${applicationinsights.connection.string:}')")
class ClientTrackingConfiguration(private val clientTrackingInterceptor: ClientTrackingInterceptor) : WebMvcConfigurer {
override fun addInterceptors(registry: InterceptorRegistry) {
log.info("Adding application insights client tracking interceptor")
registry.addInterceptor(clientTrackingInterceptor).addPathPatterns("/**")
}

private companion object {
private val log = LoggerFactory.getLogger(this::class.java)
}
}

@Configuration
class ClientTrackingInterceptor : HandlerInterceptor {
override fun preHandle(request: HttpServletRequest, response: HttpServletResponse, handler: Any): Boolean {
val token = request.getHeader(HttpHeaders.AUTHORIZATION)
if (token?.startsWith(prefix = "Bearer ", ignoreCase = true) == true) {
try {
val jwtBody = getClaimsFromJWT(token)
val user = jwtBody.getClaim("user_name")?.toString()
val client = jwtBody.getClaim("client_id")?.toString()

with(getCurrentSpan()) {
user?.run {
setAttribute("username", this) // username in customDimensions
setAttribute("enduser.id", this) // user_Id at the top level of the request
}

client?.run {
setAttribute("clientId", this)
}
}
} catch (e: ParseException) {
log.warn("problem decoding jwt public key for application insights", e)
}
}
return true
}

fun getCurrentSpan(): Span = Span.current()

@Throws(ParseException::class)
private fun getClaimsFromJWT(token: String): JWTClaimsSet =
SignedJWT.parse(token.replace("Bearer ", "")).jwtClaimsSet

private companion object {
private val log = LoggerFactory.getLogger(ClientTrackingInterceptor::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import io.swagger.v3.oas.models.security.Scopes
import io.swagger.v3.oas.models.security.SecurityRequirement
import io.swagger.v3.oas.models.security.SecurityScheme
import io.swagger.v3.oas.models.servers.Server
import io.swagger.v3.oas.models.tags.Tag
import org.springdoc.core.customizers.OpenApiCustomizer
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.info.BuildProperties
Expand All @@ -38,20 +37,10 @@ class OpenApiConfiguration(
Server().url("http://localhost:8080").description("Local"),
),
)
.tags(
listOf(
// TODO: Remove the Popular and Examples tag and start adding your own tags to group your resources
Tag().name("Popular")
.description("The most popular endpoints. Look here first when deciding which endpoint to use."),
Tag().name("Examples").description("Endpoints for searching for a prisoner within a prison"),
),
)
.info(
Info().title("HMPPS Health And Medication Api").version(version)
.contact(Contact().name("HMPPS Digital Studio").email("[email protected]")),
)
// TODO: Remove the default security schema and start adding your own schemas and roles to describe your
// service authorisation requirements
.components(
Components().addSecuritySchemes(
"bearer-jwt",
Expand Down Expand Up @@ -110,10 +99,3 @@ class OpenApiConfiguration(
}
}
}

private fun SecurityScheme.addBearerJwtRequirement(role: String): SecurityScheme = type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.`in`(SecurityScheme.In.HEADER)
.name("Authorization")
.description("A HMPPS Auth access token with the `$role` role.")
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package uk.gov.justice.digital.hmpps.healthandmedication.dto.response
import io.swagger.v3.oas.annotations.media.Schema
import uk.gov.justice.digital.hmpps.healthandmedication.dto.ReferenceDataSimpleDto

@Schema(description = "Prison person health")
@Schema(description = "Health data")
data class HealthDto(
@Schema(description = "Smoker or vaper")
val smokerOrVaper: ValueWithMetadata<ReferenceDataSimpleDto?>? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ class FoodAllergy(
result = 31 * result + allergy.hashCode()
return result
}

override fun toString(): String {
return "FoodAllergy(prisonerNumber='$prisonerNumber', allergy=$allergy, id=$id)"
}
}

data class FoodAllergies(val allergies: List<String>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ class MedicalDietaryRequirement(
result = 31 * result + dietaryRequirement.hashCode()
return result
}

override fun toString(): String {
return "MedicalDietaryRequirement(prisonerNumber='$prisonerNumber', dietaryRequirement=$dietaryRequirement, id=$id)"
}
}

data class MedicalDietaryRequirements(val medicalDietaryRequirements: List<String>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import jakarta.persistence.Column
import jakarta.persistence.Entity
import jakarta.persistence.FetchType.LAZY
import jakarta.persistence.Id
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.MapKey
import jakarta.persistence.OneToMany
import jakarta.persistence.Table
Expand All @@ -30,10 +28,6 @@ class PrisonerHealth(
@Column(name = "prisoner_number", updatable = false, nullable = false)
override val prisonerNumber: String,

@ManyToOne
@JoinColumn(name = "smoker_or_vaper", referencedColumnName = "id")
var smokerOrVaper: ReferenceDataCode? = null,

@OneToMany(mappedBy = "prisonerNumber", cascade = [ALL], orphanRemoval = true)
var foodAllergies: MutableSet<FoodAllergy> = mutableSetOf(),

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package uk.gov.justice.digital.hmpps.healthandmedication.jpa.repository

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import uk.gov.justice.digital.hmpps.healthandmedication.enums.HealthAndMedicationField
import uk.gov.justice.digital.hmpps.healthandmedication.jpa.FieldHistory
import java.util.SortedSet

@Repository
interface FieldHistoryRepository : JpaRepository<FieldHistory, Long> {
fun findAllByPrisonerNumber(prisonerNumber: String): SortedSet<FieldHistory>

fun findAllByPrisonerNumberAndField(prisonerNumber: String, field: HealthAndMedicationField): SortedSet<FieldHistory>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package uk.gov.justice.digital.hmpps.healthandmedication.jpa.repository

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import uk.gov.justice.digital.hmpps.healthandmedication.jpa.FieldMetadata

@Repository
interface FieldMetadataRepository : JpaRepository<FieldMetadata, String> {
fun findAllByPrisonerNumber(prisonerNumber: String): List<FieldMetadata>
fun deleteAllByPrisonerNumber(prisonerNumber: String)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
-- Seed data for tests
INSERT INTO reference_data_domain (code, description, list_sequence, created_at, created_by)
VALUES ('MEDICAL_DIET', 'Medical diet', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS');
VALUES ('MEDICAL_DIET', 'Medical diet', 0, '2025-01-16 00:00:00+0000', 'CONNECT_DPS');

INSERT INTO reference_data_code (id, domain, code, description, list_sequence, created_at, created_by)
VALUES
('MEDICAL_DIET_COELIAC', 'MEDICAL_DIET', 'COELIAC', 'Coeliac (cannot eat gluten)', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('MEDICAL_DIET_DIABETIC_TYPE_1', 'MEDICAL_DIET', 'DIABETIC_TYPE_1', 'Diabetic type 1', 1, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('MEDICAL_DIET_DIABETIC_TYPE_2', 'MEDICAL_DIET', 'DIABETIC_TYPE_2', 'Diabetic type 2', 2, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('MEDICAL_DIET_DYSPHAGIA', 'MEDICAL_DIET', 'DYSPHAGIA', 'Dysphagia (has problems swallowing food)', 3, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('MEDICAL_DIET_EATING_DISORDER', 'MEDICAL_DIET', 'EATING_DISORDER', 'Eating disorder', 4, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('MEDICAL_DIET_LACTOSE_INTOLERANT', 'MEDICAL_DIET', 'LACTOSE_INTOLERANT', 'Lactose intolerant', 5, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('MEDICAL_DIET_LOW_CHOLESTEROL', 'MEDICAL_DIET', 'LOW_CHOLESTEROL', 'Low cholesterol', 6, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('MEDICAL_DIET_LOW_PHOSPHOROUS', 'MEDICAL_DIET', 'LOW_PHOSPHOROUS', 'Low phosphorous diet', 7, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('MEDICAL_DIET_NUTRIENT_DEFICIENCY', 'MEDICAL_DIET', 'NUTRIENT_DEFICIENCY', 'Nutrient deficiency', 7, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('MEDICAL_DIET_OTHER', 'MEDICAL_DIET', 'OTHER', 'Other', 8, '2025-01-16 00:00:00+0100', 'CONNECT_DPS');
('MEDICAL_DIET_COELIAC', 'MEDICAL_DIET', 'COELIAC', 'Coeliac (cannot eat gluten)', 0, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('MEDICAL_DIET_DIABETIC_TYPE_1', 'MEDICAL_DIET', 'DIABETIC_TYPE_1', 'Diabetic type 1', 1, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('MEDICAL_DIET_DIABETIC_TYPE_2', 'MEDICAL_DIET', 'DIABETIC_TYPE_2', 'Diabetic type 2', 2, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('MEDICAL_DIET_DYSPHAGIA', 'MEDICAL_DIET', 'DYSPHAGIA', 'Dysphagia (has problems swallowing food)', 3, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('MEDICAL_DIET_EATING_DISORDER', 'MEDICAL_DIET', 'EATING_DISORDER', 'Eating disorder', 4, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('MEDICAL_DIET_LACTOSE_INTOLERANT', 'MEDICAL_DIET', 'LACTOSE_INTOLERANT', 'Lactose intolerant', 5, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('MEDICAL_DIET_LOW_CHOLESTEROL', 'MEDICAL_DIET', 'LOW_CHOLESTEROL', 'Low cholesterol', 6, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('MEDICAL_DIET_LOW_PHOSPHOROUS', 'MEDICAL_DIET', 'LOW_PHOSPHOROUS', 'Low phosphorous diet', 7, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('MEDICAL_DIET_NUTRIENT_DEFICIENCY', 'MEDICAL_DIET', 'NUTRIENT_DEFICIENCY', 'Nutrient deficiency', 7, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('MEDICAL_DIET_OTHER', 'MEDICAL_DIET', 'OTHER', 'Other', 8, '2025-01-16 00:00:00+0000', 'CONNECT_DPS');

33 changes: 16 additions & 17 deletions src/main/resources/db/migration/common/V8__food_allergy_data.sql
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
-- Seed data for tests
INSERT INTO reference_data_domain (code, description, list_sequence, created_at, created_by)
VALUES ('FOOD_ALLERGY', 'Food allergy', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS');
VALUES ('FOOD_ALLERGY', 'Food allergy', 0, '2025-01-16 00:00:00+0000', 'CONNECT_DPS');

INSERT INTO reference_data_code (id, domain, code, description, list_sequence, created_at, created_by)
VALUES
('FOOD_ALLERGY_CELERY', 'FOOD_ALLERGY', 'CELERY', 'Celery', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_GLUTEN', 'FOOD_ALLERGY', 'GLUTEN', 'Cereals containing gluten', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_CRUSTACEANS', 'FOOD_ALLERGY', 'CRUSTACEANS', 'Crustaceans', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_EGG', 'FOOD_ALLERGY', 'EGG', 'Egg', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_FISH', 'FOOD_ALLERGY', 'FISH', 'Fish', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_LUPIN', 'FOOD_ALLERGY', 'LUPIN', 'Lupin', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_MILK', 'FOOD_ALLERGY', 'MILK', 'Milk', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_MOLLUSCS', 'FOOD_ALLERGY', 'MOLLUSCS', 'Molluscs', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_MUSTARD', 'FOOD_ALLERGY', 'MUSTARD', 'Mustard', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_PEANUTS', 'FOOD_ALLERGY', 'PEANUTS', 'Peanuts', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_SESAME', 'FOOD_ALLERGY', 'SESAME', 'Sesame', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_SOYA', 'FOOD_ALLERGY', 'SOYA', 'Soya', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_SULPHUR_DIOXIDE', 'FOOD_ALLERGY', 'SULPHUR_DIOXIDE', 'Sulphur dioxide', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_TREE_NUTS', 'FOOD_ALLERGY', 'TREE_NUTS', 'Tree nuts', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS'),
('FOOD_ALLERGY_OTHER', 'FOOD_ALLERGY', 'OTHER', 'Other', 0, '2025-01-16 00:00:00+0100', 'CONNECT_DPS');
('FOOD_ALLERGY_CELERY', 'FOOD_ALLERGY', 'CELERY', 'Celery', 0, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_GLUTEN', 'FOOD_ALLERGY', 'GLUTEN', 'Cereals containing gluten', 1, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_CRUSTACEANS', 'FOOD_ALLERGY', 'CRUSTACEANS', 'Crustaceans', 2, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_EGG', 'FOOD_ALLERGY', 'EGG', 'Egg', 3, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_FISH', 'FOOD_ALLERGY', 'FISH', 'Fish', 4, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_LUPIN', 'FOOD_ALLERGY', 'LUPIN', 'Lupin', 5, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_MILK', 'FOOD_ALLERGY', 'MILK', 'Milk', 6, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_MOLLUSCS', 'FOOD_ALLERGY', 'MOLLUSCS', 'Molluscs', 7, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_MUSTARD', 'FOOD_ALLERGY', 'MUSTARD', 'Mustard', 8, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_PEANUTS', 'FOOD_ALLERGY', 'PEANUTS', 'Peanuts', 9, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_SESAME', 'FOOD_ALLERGY', 'SESAME', 'Sesame', 10, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_SOYA', 'FOOD_ALLERGY', 'SOYA', 'Soya', 11, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_SULPHUR_DIOXIDE', 'FOOD_ALLERGY', 'SULPHUR_DIOXIDE', 'Sulphur dioxide', 12, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_TREE_NUTS', 'FOOD_ALLERGY', 'TREE_NUTS', 'Tree nuts', 13, '2025-01-16 00:00:00+0000', 'CONNECT_DPS'),
('FOOD_ALLERGY_OTHER', 'FOOD_ALLERGY', 'OTHER', 'Other', 14, '2025-01-16 00:00:00+0000', 'CONNECT_DPS');

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package uk.gov.justice.digital.hmpps.healthandmedication.client.prisonersearch

import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.springframework.web.reactive.function.client.WebClient
import org.springframework.web.reactive.function.client.WebClientResponseException
import uk.gov.justice.digital.hmpps.healthandmedication.client.prisonersearch.dto.PrisonerDto
import uk.gov.justice.digital.hmpps.healthandmedication.config.DownstreamServiceException
import uk.gov.justice.digital.hmpps.healthandmedication.integration.wiremock.PRISONER_NUMBER
import uk.gov.justice.digital.hmpps.healthandmedication.integration.wiremock.PRISONER_NUMBER_NOT_FOUND
import uk.gov.justice.digital.hmpps.healthandmedication.integration.wiremock.PRISONER_NUMBER_THROW_EXCEPTION
import uk.gov.justice.digital.hmpps.healthandmedication.integration.wiremock.PRISON_ID
import uk.gov.justice.digital.hmpps.healthandmedication.integration.wiremock.PrisonerSearchServer

class PrisonerSearchClientTest {
private lateinit var client: PrisonerSearchClient

@BeforeEach
fun resetMocks() {
server.resetRequests()
val webClient = WebClient.create("http://localhost:${server.port()}")
client = PrisonerSearchClient(webClient)
}

@Test
fun `getPrisoner - success`() {
server.stubGetPrisoner()

val result = client.getPrisoner(PRISONER_NUMBER)

assertThat(result!!).isEqualTo(PrisonerDto(prisonerNumber = PRISONER_NUMBER, prisonId = PRISON_ID))
}

@Test
fun `getPrisoner - prisoner not found`() {
val result = client.getPrisoner(PRISONER_NUMBER_NOT_FOUND)

assertThat(result).isNull()
}

@Test
fun `getPrisoner - downstream service exception`() {
server.stubGetPrisonerException()

assertThatThrownBy { client.getPrisoner(PRISONER_NUMBER_THROW_EXCEPTION) }
.isInstanceOf(DownstreamServiceException::class.java)
.hasMessage("Get prisoner request failed")
.hasCauseInstanceOf(WebClientResponseException::class.java)
.hasRootCauseMessage("500 Internal Server Error from GET http://localhost:8112/prisoner/${PRISONER_NUMBER_THROW_EXCEPTION}")
}

companion object {
@JvmField
internal val server = PrisonerSearchServer()

@BeforeAll
@JvmStatic
fun startMocks() {
server.start()
}

@AfterAll
@JvmStatic
fun stopMocks() {
server.stop()
}
}
}
Loading

0 comments on commit e5f8dd1

Please sign in to comment.