diff --git a/build.gradle.kts b/build.gradle.kts index b691a15..194c261 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -40,6 +40,7 @@ testing { exclude(group = "io.swagger.core.v3") exclude(group = "io.swagger.parser.v3", module = "swagger-parser-safe-url-resolver") } + implementation("org.mockito.kotlin:mockito-kotlin:5.4.0") implementation("org.springframework.boot:spring-boot-testcontainers") implementation("org.testcontainers:localstack") } diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerMother.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerMother.kt new file mode 100644 index 0000000..fc500ba --- /dev/null +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/domain/EmployerMother.kt @@ -0,0 +1,43 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain + +object EmployerMother { + val tesco = Employer( + id = "89de6c84-3372-4546-bbc1-9d1dc9ceb354", + name = "Tesco", + description = "Tesco plc is a British multinational groceries and general merchandise retailer headquartered in Welwyn Garden City, England. The company was founded by Jack Cohen in Hackney, London in 1919.", + sector = "RETAIL", + status = "SILVER", + ) + + val tescoLogistics = Employer( + id = "2c8032bf-e583-4ae9-bcec-968a1c4881f9", + name = "Tesco", + description = "This is another Tesco employer that provides logistic services.", + sector = "LOGISTICS", + status = "GOLD", + ) + + val sainsburys = Employer( + id = "f4fbdbf3-823c-4877-aafc-35a7fa74a15a", + name = "Sainsbury's", + description = "J Sainsbury plc, trading as Sainsbury's, is a British supermarket and the second-largest chain of supermarkets in the United Kingdom. Founded in 1869 by John James Sainsbury with a shop in Drury Lane, London, the company was the largest UK retailer of groceries for most of the 20th century.", + sector = "RETAIL", + status = "GOLD", + ) + + val amazon = Employer( + id = "bf392249-b360-4e3e-81a0-8497047987e8", + name = "Amazon", + description = "Amazon.com, Inc., doing business as Amazon, is an American multinational technology company, engaged in e-commerce, cloud computing, online advertising, digital streaming, and artificial intelligence.", + sector = "LOGISTICS", + status = "KEY_PARTNER", + ) + + val abcConstruction = Employer( + id = "182e9a24-6edb-48a6-a84f-b7061f004a97", + name = "ABC Construction", + description = "This is a description", + sector = "CONSTRUCTION", + status = "SILVER", + ) +} diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt index baaff4b..c71727d 100644 --- a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt @@ -1,6 +1,9 @@ package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.junit.jupiter.MockitoExtension +import org.mockito.kotlin.whenever import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT @@ -8,6 +11,7 @@ import org.springframework.http.HttpHeaders import org.springframework.test.context.ActiveProfiles import org.springframework.test.context.DynamicPropertyRegistry import org.springframework.test.context.DynamicPropertySource +import org.springframework.test.context.bean.override.mockito.MockitoSpyBean import org.springframework.test.web.reactive.server.WebTestClient import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.testcontainers.LocalStackContainer import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.testcontainers.LocalStackContainer.setLocalStackProperties @@ -15,9 +19,20 @@ import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.ExampleApiExtension.Companion.exampleApi import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.HmppsAuthApiExtension import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.HmppsAuthApiExtension.Companion.hmppsAuth +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.JobsBoardApiExtension +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.application.DefaultTimeProvider import uk.gov.justice.hmpps.test.kotlin.auth.JwtAuthorisationHelper +import java.time.Instant +import java.time.LocalDateTime +import java.time.ZoneId +import java.util.* -@ExtendWith(HmppsAuthApiExtension::class, ExampleApiExtension::class) +@ExtendWith( + HmppsAuthApiExtension::class, + ExampleApiExtension::class, + JobsBoardApiExtension::class, + MockitoExtension::class, +) @SpringBootTest(webEnvironment = RANDOM_PORT) @ActiveProfiles("test") abstract class IntegrationTestBase { @@ -28,6 +43,13 @@ abstract class IntegrationTestBase { @Autowired protected lateinit var jwtAuthHelper: JwtAuthorisationHelper + @MockitoSpyBean + protected lateinit var timeProvider: DefaultTimeProvider + + val defaultTimezoneId = ZoneId.of("Z") + val defaultCurrentTime: Instant = Instant.parse("2024-01-01T00:00:00Z") + val defaultCurrentTimeLocal: LocalDateTime get() = defaultCurrentTime.atZone(defaultTimezoneId).toLocalDateTime() + companion object { private val localStackContainer by lazy { LocalStackContainer.instance } @@ -38,6 +60,12 @@ abstract class IntegrationTestBase { } } + @BeforeEach + internal fun setUp() { + whenever(timeProvider.timezoneId).thenReturn(defaultTimezoneId) + whenever(timeProvider.now()).thenReturn(defaultCurrentTimeLocal) + } + internal fun setAuthorisation( username: String? = "AUTH_ADM", roles: List = listOf(), @@ -48,4 +76,6 @@ abstract class IntegrationTestBase { hmppsAuth.stubHealthPing(status) exampleApi.stubHealthPing(status) } + + protected fun randomUUID() = UUID.randomUUID().toString() } diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/shared/infrastructure/JobsBoardApiWebClientShould.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/shared/infrastructure/JobsBoardApiWebClientShould.kt new file mode 100644 index 0000000..8e0ff70 --- /dev/null +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/shared/infrastructure/JobsBoardApiWebClientShould.kt @@ -0,0 +1,46 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.shared.infrastructure + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.springframework.beans.factory.annotation.Autowired +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerMother.sainsburys +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.IntegrationTestBase +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.HmppsAuthApiExtension.Companion.hmppsAuth +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.JobsBoardApiExtension.Companion.jobsBoardApi +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure.JobsBoardApiWebClient + +class JobsBoardApiWebClientShould : IntegrationTestBase() { + + @Autowired + private lateinit var jobsBoardApiWebClient: JobsBoardApiWebClient + + @Nested + @DisplayName("JobsBoard `GET` /employers") + inner class EmployersGetEndpoint { + @Test + fun `return employer details, given valid employer ID`() { + val employer = sainsburys.copy(createdAt = timeProvider.nowAsInstant()) + + hmppsAuth.stubGrantToken() + jobsBoardApi.stubRetrieveEmployer(employer) + + val actualEmployer = jobsBoardApiWebClient.getEmployer(employer.id) + + assertThat(actualEmployer).isEqualTo(employer) + } + + @Test + fun `return nothing, given invalid employer ID`() { + val employerId = randomUUID() + + hmppsAuth.stubGrantToken() + jobsBoardApi.stubRetrieveEmployerNotFound() + + val actualEmployer = jobsBoardApiWebClient.getEmployer(employerId) + + assertThat(actualEmployer).isNull() + } + } +} diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/wiremock/JobsBoardApiMockServer.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/wiremock/JobsBoardApiMockServer.kt new file mode 100644 index 0000000..3075ef8 --- /dev/null +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/wiremock/JobsBoardApiMockServer.kt @@ -0,0 +1,58 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock + +import com.github.tomakehurst.wiremock.WireMockServer +import com.github.tomakehurst.wiremock.client.WireMock.aResponse +import com.github.tomakehurst.wiremock.client.WireMock.get +import com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching +import org.junit.jupiter.api.extension.AfterAllCallback +import org.junit.jupiter.api.extension.BeforeAllCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.Employer + +class JobsBoardApiMockServer : WireMockServer(8092) { + private val retrieveEmployerPathRegex = "/employers/[a-zA-Z0-9\\-]*" + + fun stubRetrieveEmployer(employer: Employer) { + stubFor( + get(urlPathMatching(retrieveEmployerPathRegex)) + .willReturn( + aResponse() + .withHeader("Content-Type", "application/json") + .withBody(employer.response()), + ), + ) + } + + fun stubRetrieveEmployerNotFound() { + stubFor( + get(urlPathMatching(retrieveEmployerPathRegex)) + .willReturn( + aResponse() + .withStatus(404), + ), + ) + } +} + +class JobsBoardApiExtension : BeforeAllCallback, AfterAllCallback, BeforeEachCallback { + companion object { + @JvmField + val jobsBoardApi = JobsBoardApiMockServer() + } + + override fun beforeAll(context: ExtensionContext): Unit = jobsBoardApi.start() + override fun beforeEach(context: ExtensionContext): Unit = jobsBoardApi.resetAll() + override fun afterAll(context: ExtensionContext): Unit = jobsBoardApi.stop() +} + +private fun Employer.response() = """ + { + "id": "$id", + "name": "$name", + "description": "$description", + "sector": "$sector", + "status": "$status", + "createdAt": ${createdAt?.let { "\"$it\"" }} + } +""".trimIndent() diff --git a/src/integrationTest/resources/application-test.yml b/src/integrationTest/resources/application-test.yml index b69908b..a47c60e 100644 --- a/src/integrationTest/resources/application-test.yml +++ b/src/integrationTest/resources/application-test.yml @@ -21,3 +21,13 @@ hmpps.sqs: queues: integrationqueue: queueName: hmpps_jobs_board_integration_queue + +api: + base.url: + jobsboard: "http://localhost:8092" + client: + id: "api-client" + secret: "api-client-secret" + + integration: + enabled: true \ No newline at end of file diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/config/WebClientConfiguration.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/config/WebClientConfiguration.kt index 1398519..4fe54f0 100644 --- a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/config/WebClientConfiguration.kt +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/config/WebClientConfiguration.kt @@ -13,6 +13,7 @@ import java.time.Duration class WebClientConfiguration( @Value("\${example-api.url}") val exampleApiBaseUri: String, @Value("\${hmpps-auth.url}") val hmppsAuthBaseUri: String, + @Value("\${api.base.url.jobsboard}") val jobsboardApiBaseUri: String, @Value("\${api.health-timeout:2s}") val healthTimeout: Duration, @Value("\${api.timeout:20s}") val timeout: Duration, ) { @@ -29,4 +30,8 @@ class WebClientConfiguration( @Bean fun exampleApiWebClient(authorizedClientManager: OAuth2AuthorizedClientManager, builder: WebClient.Builder): WebClient = builder.authorisedWebClient(authorizedClientManager, registrationId = "example-api", url = exampleApiBaseUri, timeout) + + @Bean("jobsBoardWebClient") + fun jobsBoardApiWebClient(authorizedClientManager: OAuth2AuthorizedClientManager, builder: WebClient.Builder): WebClient = + builder.authorisedWebClient(authorizedClientManager, registrationId = "hmpps-jobs-board-api", url = jobsboardApiBaseUri, timeout) } diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerMessageServices.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerMessageServices.kt index 049a7a5..85112c1 100644 --- a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerMessageServices.kt +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerMessageServices.kt @@ -56,8 +56,12 @@ class EmployerCreationMessageService( override fun handleEvent(employerEvent: EmployerEvent) { log.info("handle employer creation event; eventId={}", employerEvent.eventId) - retriever.retrieve(employerEvent.employerId).also { employer -> - registrar.registerCreation(employer) + try { + retriever.retrieve(employerEvent.employerId).also { employer -> + registrar.registerCreation(employer) + } + } catch (e: Exception) { + throw Exception("Error at employer creation event: eventId=${employerEvent.eventId}", e) } } } diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerRetriever.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerRetriever.kt index e1ffb29..9b0f7dd 100644 --- a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerRetriever.kt +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerRetriever.kt @@ -4,9 +4,12 @@ import org.springframework.stereotype.Service import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.Employer @Service -class EmployerRetriever { +class EmployerRetriever( + private val employerService: EmployerService, +) { fun retrieve(id: String): Employer { - // TODO implement employer retrieval from MJMA jobs board API - throw NotImplementedError("Employer's retrieval is not yet implemented!") + return employerService.retrieveById(id) ?: run { + throw IllegalArgumentException("Employer id=$id not found") + } } } diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerService.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerService.kt new file mode 100644 index 0000000..fedb244 --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerService.kt @@ -0,0 +1,12 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.application + +import org.springframework.stereotype.Service +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.Employer +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.domain.JobsBoardApiClient + +@Service +class EmployerService( + private val jobsBoardApiClient: JobsBoardApiClient, +) { + fun retrieveById(id: String): Employer? = jobsBoardApiClient.getEmployer(id) +} diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/domain/JobsBoardApiClient.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/domain/JobsBoardApiClient.kt new file mode 100644 index 0000000..d48add8 --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/domain/JobsBoardApiClient.kt @@ -0,0 +1,7 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.domain + +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.Employer + +interface JobsBoardApiClient { + fun getEmployer(id: String): Employer? +} diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClient.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClient.kt new file mode 100644 index 0000000..f3420b1 --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClient.kt @@ -0,0 +1,65 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure + +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.springframework.beans.factory.annotation.Qualifier +import org.springframework.http.MediaType.APPLICATION_JSON +import org.springframework.stereotype.Service +import org.springframework.web.reactive.function.client.WebClient +import org.springframework.web.reactive.function.client.WebClientResponseException +import reactor.core.publisher.Mono +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.Employer +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.domain.JobsBoardApiClient +import java.time.Instant + +@Service +class JobsBoardApiWebClient( + @Qualifier("jobsBoardWebClient") private val jobsBoardWebClient: WebClient, +) : JobsBoardApiClient { + + companion object { + val log: Logger = LoggerFactory.getLogger(this::class.java) + } + + override fun getEmployer(id: String): Employer? { + log.debug("Getting employer details with id={}", id) + return jobsBoardWebClient + .get().uri("/employers/{id}", id).accept(APPLICATION_JSON).retrieve() + .bodyToMono(GetEmployerResponse::class.java) + .onErrorResume(WebClientResponseException.NotFound::class.java) { + log.debug("Employer not found. employerId={}", id) + Mono.empty() + }.block()?.employer() + } +} + +data class GetEmployerResponse( + val id: String, + val name: String, + val description: String, + val sector: String, + val status: String, + val createdAt: String, +) { + companion object { + fun from(employer: Employer): GetEmployerResponse { + return GetEmployerResponse( + id = employer.id, + name = employer.name, + description = employer.description, + sector = employer.sector, + status = employer.status, + createdAt = employer.createdAt.toString(), + ) + } + } + + fun employer() = Employer( + id = id, + name = name, + description = description, + sector = sector, + status = status, + createdAt = Instant.parse(createdAt), + ) +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index af27b7e..3e7156d 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -9,5 +9,9 @@ example-api: id: "example-api-client" secret: "example-api-client-secret" -api.integration: - enabled: false +api: + base.url: + jobsboard: "https://jobs-board-api-dev.hmpps.service.justice.gov.uk" + + integration: + enabled: false diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 3796ff9..8c0b1e1 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -34,6 +34,12 @@ spring: client-secret: ${example-api.client.secret} authorization-grant-type: client_credentials scope: read + hmpps-jobs-board-api: + provider: hmpps-auth + client-id: ${api.client.id} + client-secret: ${api.client.secret} + authorization-grant-type: client_credentials + scope: read profiles: group: diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerCreationMessageServiceShould.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerCreationMessageServiceShould.kt index 3bd0a16..79dd2e7 100644 --- a/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerCreationMessageServiceShould.kt +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/employers/application/EmployerCreationMessageServiceShould.kt @@ -17,6 +17,7 @@ import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.Emp import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerEvent import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerEventType import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerMother.sainsburys +import kotlin.test.assertFailsWith @ExtendWith(MockitoExtension::class) class EmployerCreationMessageServiceShould : EmployerMessageServiceTestCase() { @@ -24,6 +25,13 @@ class EmployerCreationMessageServiceShould : EmployerMessageServiceTestCase() { @InjectMocks private lateinit var creationService: EmployerCreationMessageService + @BeforeEach + internal fun setUp() { + whenever(mockedObjectMapper.readValue(anyString(), eq(EmployerEvent::class.java))).thenAnswer { + objectMapper.readValue(it.arguments[0] as String, EmployerEvent::class.java) + } + } + @Nested @DisplayName("Given a new employer") inner class GivenANewEmployer { @@ -31,9 +39,6 @@ class EmployerCreationMessageServiceShould : EmployerMessageServiceTestCase() { @BeforeEach internal fun setUp() { - whenever(mockedObjectMapper.readValue(anyString(), eq(EmployerEvent::class.java))).thenAnswer { - objectMapper.readValue(it.arguments[0] as String, EmployerEvent::class.java) - } whenever(employerRetriever.retrieve(employer.id)).thenReturn(employer) } @@ -56,6 +61,20 @@ class EmployerCreationMessageServiceShould : EmployerMessageServiceTestCase() { } } + @Test + fun `receive and hit error handling event, with invalid employer ID`() { + val employerId = randomUUID() + val employerEvent = employerCreationEvent(employerId) + val errorCause = IllegalArgumentException("Employer id=$employerId not found") + whenever(employerRetriever.retrieve(anyString())).thenThrow(errorCause) + + val exception = assertFailsWith { + creationService.handleMessage(employerEvent.toIntegrationEvent(), employerEvent.messageAttributes()) + } + assertEquals("Error at employer creation event: eventId=${employerEvent.eventId}", exception.message) + assertEquals(errorCause, exception.cause) + } + private fun employerCreationEvent(employerId: String) = EmployerEvent( eventId = randomUUID(), eventType = EmployerEventType.EMPLOYER_CREATED, diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClientShould.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClientShould.kt new file mode 100644 index 0000000..5682a89 --- /dev/null +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClientShould.kt @@ -0,0 +1,30 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure + +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.junit.jupiter.MockitoExtension +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.employers.domain.EmployerMother.sainsburys + +@ExtendWith(MockitoExtension::class) +class JobsBoardApiWebClientShould : JobsBoardApiWebClientTestBase() { + + @Nested + inner class GivenAnEmployer { + private val employer = sainsburys.copy(createdAt = defaultCurrentTime) + private val uri = "/employers/{id}" + + @Test + fun `return employer details with a valid employer ID`() { + replyOnGetEmployerById(GetEmployerResponse.from(employer), employer.id) + + val actualEmployer = jobsBoardApiWebClient.getEmployer(employer.id) + + assertThat(actualEmployer).isEqualTo(employer) + } + + private fun replyOnGetEmployerById(response: GetEmployerResponse, employerId: String) = + replyOnRequestById(response.javaClass, response, uri, employerId) + } +} diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClientTestBase.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClientTestBase.kt new file mode 100644 index 0000000..98cff2b --- /dev/null +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/shared/infrastructure/JobsBoardApiWebClientTestBase.kt @@ -0,0 +1,38 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.infrastructure + +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.kotlin.whenever +import org.springframework.http.MediaType.APPLICATION_JSON +import org.springframework.web.reactive.function.client.WebClient +import reactor.core.publisher.Mono +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.shared.application.UnitTestBase + +abstract class JobsBoardApiWebClientTestBase : UnitTestBase() { + @Mock + protected lateinit var webClient: WebClient + + @InjectMocks + protected lateinit var jobsBoardApiWebClient: JobsBoardApiWebClient + + val requestUriMock = mock(WebClient.RequestHeadersUriSpec::class.java) + val requestHeadersMock = mock(WebClient.RequestHeadersSpec::class.java) + val responseSpecMock = mock(WebClient.ResponseSpec::class.java) + + private fun onRequest() { + whenever(webClient.get()).thenReturn(requestUriMock) + whenever(requestHeadersMock.accept(APPLICATION_JSON)).thenReturn(requestHeadersMock) + whenever(requestHeadersMock.retrieve()).thenReturn(responseSpecMock) + } + + private fun onRequestById(uri: String, id: String) { + onRequest() + whenever(requestUriMock.uri(uri, id)).thenReturn(requestHeadersMock) + } + + protected fun replyOnRequestById(elementClass: Class, response: T & Any, uri: String, id: String) { + onRequestById(uri, id) + whenever(responseSpecMock.bodyToMono(elementClass)).thenReturn(Mono.just(response)) + } +} diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 0000000..26e88a4 --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,13 @@ + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n + + + + + + + + + \ No newline at end of file