diff --git a/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/config/MessagingConfiguration.kt b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/config/MessagingConfiguration.kt index 7ebe9f95..e056c719 100644 --- a/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/config/MessagingConfiguration.kt +++ b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/config/MessagingConfiguration.kt @@ -9,13 +9,14 @@ import org.springframework.context.annotation.Configuration import software.amazon.awssdk.services.sqs.SqsAsyncClient import uk.gov.dluhc.messagingsupport.MessageQueue import uk.gov.dluhc.messagingsupport.MessagingConfigurationHelper +import uk.gov.dluhc.registercheckerapi.messaging.models.PendingRegisterCheckArchiveMessage import uk.gov.dluhc.registercheckerapi.messaging.models.RegisterCheckResultMessage @Configuration class MessagingConfiguration { @Value("\${sqs.confirm-applicant-register-check-result-queue-name}") - private lateinit var confirmRegisterCheckResultQueueName: String + private lateinit var pendingRegisterCheckResultQueueName: String @Value("\${sqs.postal-vote-confirm-applicant-register-check-result-queue-name}") private lateinit var postalVoteConfirmRegisterCheckResultQueueName: String @@ -29,9 +30,12 @@ class MessagingConfiguration { @Value("\${sqs.register-check-result-response-queue-name}") private lateinit var registerCheckResultResponseQueueName: String + @Value("\${sqs.pending-register-check-archive-queue-name}") + private lateinit var pendingRegisterCheckArchiveQueueName: String + @Bean(name = ["confirmRegisterCheckResultQueue"]) fun confirmRegisterCheckResultQueue(sqsTemplate: SqsTemplate) = - MessageQueue(confirmRegisterCheckResultQueueName, sqsTemplate) + MessageQueue(pendingRegisterCheckResultQueueName, sqsTemplate) @Bean(name = ["postalVoteConfirmRegisterCheckResultQueue"]) fun postalVoteConfirmRegisterCheckResultQueue(sqsTemplate: SqsTemplate) = @@ -49,6 +53,10 @@ class MessagingConfiguration { fun registerCheckResultResponseQueue(sqsTemplate: SqsTemplate) = MessageQueue(registerCheckResultResponseQueueName, sqsTemplate) + @Bean(name = ["pendingRegisterCheckArchiveQueue"]) + fun pendingRegisterCheckArchiveQueue(sqsTemplate: SqsTemplate) = + MessageQueue(pendingRegisterCheckResultQueueName, sqsTemplate) + @Bean fun sqsMessagingMessageConverter( objectMapper: ObjectMapper, diff --git a/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/exception/PendingRegisterCheckArchiveInvalidStatusException.kt b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/exception/PendingRegisterCheckArchiveInvalidStatusException.kt new file mode 100644 index 00000000..0792d90b --- /dev/null +++ b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/exception/PendingRegisterCheckArchiveInvalidStatusException.kt @@ -0,0 +1,7 @@ +package uk.gov.dluhc.emsintegrationapi.exception + +import uk.gov.dluhc.emsintegrationapi.database.entity.CheckStatus + +class PendingRegisterCheckArchiveInvalidStatusException( + status: CheckStatus, +) : IllegalStateException("Register Check is at status $status so cannot be archived (must be at status PENDING") diff --git a/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/exception/PendingRegisterCheckNotFoundException.kt b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/exception/PendingRegisterCheckNotFoundException.kt index 829152dc..0b0ee10c 100644 --- a/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/exception/PendingRegisterCheckNotFoundException.kt +++ b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/exception/PendingRegisterCheckNotFoundException.kt @@ -5,5 +5,6 @@ import java.util.UUID /** * Thrown if a pending register check for a given correlationId does not exist. */ -class PendingRegisterCheckNotFoundException(correlationId: UUID) : - RuntimeException("Pending register check for requestid:[$correlationId] not found") +class PendingRegisterCheckNotFoundException( + correlationId: UUID, +) : RuntimeException("Pending register check for requestid:[$correlationId] not found") diff --git a/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/MessageQueueResolver.kt b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/MessageQueueResolver.kt index 8bad4155..6e3495b0 100644 --- a/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/MessageQueueResolver.kt +++ b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/MessageQueueResolver.kt @@ -3,6 +3,7 @@ package uk.gov.dluhc.emsintegrationapi.messaging import org.springframework.beans.factory.annotation.Qualifier import org.springframework.stereotype.Component import uk.gov.dluhc.messagingsupport.MessageQueue +import uk.gov.dluhc.registercheckerapi.messaging.models.PendingRegisterCheckArchiveMessage import uk.gov.dluhc.registercheckerapi.messaging.models.RegisterCheckResultMessage import uk.gov.dluhc.registercheckerapi.messaging.models.SourceType import uk.gov.dluhc.registercheckerapi.messaging.models.SourceType.APPLICATIONS_MINUS_API @@ -18,6 +19,7 @@ class MessageQueueResolver( @Qualifier("proxyVoteConfirmRegisterCheckResultQueue") private val proxyVoteConfirmRegisterCheckResultQueue: MessageQueue, @Qualifier("overseasVoteConfirmRegisterCheckResultQueue") private val overseasVoteConfirmRegisterCheckResultQueue: MessageQueue, @Qualifier("registerCheckResultResponseQueue") private val registerCheckResultResponseQueue: MessageQueue, + @Qualifier("pendingRegisterCheckArchiveQueue") private val pendingRegisterCheckArchiveQueue: MessageQueue, ) { fun getTargetQueueForSourceType(sourceType: SourceType) = diff --git a/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/PendingRegisterCheckArchiveDataMessageListener.kt b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/PendingRegisterCheckArchiveDataMessageListener.kt new file mode 100644 index 00000000..bee7322c --- /dev/null +++ b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/PendingRegisterCheckArchiveDataMessageListener.kt @@ -0,0 +1,33 @@ +package uk.gov.dluhc.emsintegrationapi.messaging + +import io.awspring.cloud.sqs.annotation.SqsListener +import jakarta.validation.Valid +import mu.KotlinLogging +import org.springframework.messaging.handler.annotation.Payload +import org.springframework.stereotype.Component +import uk.gov.dluhc.emsintegrationapi.service.PendingRegisterCheckArchiveService +import uk.gov.dluhc.messagingsupport.MessageListener +import uk.gov.dluhc.registercheckerapi.messaging.models.PendingRegisterCheckArchiveMessage + +private val logger = KotlinLogging.logger { } + +/** + * Implementation of [MessageListener] to handle [PendingRegisterCheckArchiveMessage] messages + */ +@Component +class PendingRegisterCheckArchiveDataMessageListener( + private val pendingRegisterCheckArchiveService: PendingRegisterCheckArchiveService, +) : MessageListener { + @SqsListener("\${sqs.pending-register-check-archive-queue-name}") + override fun handleMessage( + @Valid @Payload payload: PendingRegisterCheckArchiveMessage, + ) { + with(payload) { + logger.info { + "New PendingRegisterCheckArchiveMessage received with " + + "correlationId: $correlationId" + } + pendingRegisterCheckArchiveService.archiveIfStatusIsPending(correlationId) + } + } +} diff --git a/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/service/PendingRegisterCheckArchiveService.kt b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/service/PendingRegisterCheckArchiveService.kt new file mode 100644 index 00000000..c6aeac7d --- /dev/null +++ b/src/main/kotlin/uk/gov/dluhc/emsintegrationapi/service/PendingRegisterCheckArchiveService.kt @@ -0,0 +1,38 @@ +package uk.gov.dluhc.emsintegrationapi.service + +import mu.KotlinLogging +import org.springframework.stereotype.Service +import uk.gov.dluhc.emsintegrationapi.database.entity.CheckStatus +import uk.gov.dluhc.emsintegrationapi.database.repository.RegisterCheckRepository +import uk.gov.dluhc.emsintegrationapi.exception.PendingRegisterCheckArchiveInvalidStatusException +import uk.gov.dluhc.emsintegrationapi.exception.PendingRegisterCheckNotFoundException +import java.time.Instant +import java.util.UUID + +private val logger = KotlinLogging.logger { } + +@Service +class PendingRegisterCheckArchiveService( + private val registerCheckRepository: RegisterCheckRepository, +) { + fun archiveIfStatusIsPending(correlationId: UUID?) { + val corrid = correlationId ?: throw IllegalArgumentException("Correlation ID is null") + val registerCheck = registerCheckRepository.findByCorrelationId(corrid) + if (registerCheck == null) { + logger.warn { + "Pending register check for requestid:[$correlationId] not found" + } + throw PendingRegisterCheckNotFoundException(corrid) + } + if (registerCheck.status == CheckStatus.PENDING) { + registerCheck.status = CheckStatus.ARCHIVED + registerCheck.matchResultSentAt = Instant.now() + registerCheckRepository.save(registerCheck) + } else { + logger.warn { + "Register Check with correlationId $correlationId has status ${registerCheck.status} so cannot be archived (must be at status PENDING)" + } + throw PendingRegisterCheckArchiveInvalidStatusException(registerCheck.status) + } + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b5cd8eb8..04ef3210 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -40,6 +40,7 @@ sqs: overseas-vote-confirm-applicant-register-check-result-queue-name: ${SQS_OVERSEAS_VOTE_CONFIRM_APPLICANT_REGISTER_CHECK_RESULT_QUEUE_NAME} register-check-result-response-queue-name: ${SQS_REGISTER_CHECK_RESULT_RESPONSE_QUEUE_NAME} remove-applicant-register-check-data-queue-name: ${SQS_REMOVE_APPLICANT_REGISTER_CHECK_DATA_QUEUE_NAME} + pending-register-check-archive-queue-name: ${SQS_PENDING_REGISTER_CHECK_AERCHIVE_QUEUE_NAME} api: ero-management: diff --git a/src/main/resources/openapi/registerchecker/sqs/rca-sqs-messaging.yaml b/src/main/resources/openapi/registerchecker/sqs/rca-sqs-messaging.yaml index b0641b39..d83bd0b6 100644 --- a/src/main/resources/openapi/registerchecker/sqs/rca-sqs-messaging.yaml +++ b/src/main/resources/openapi/registerchecker/sqs/rca-sqs-messaging.yaml @@ -161,6 +161,20 @@ components: - registerCheckResult - matches + PendingRegisterCheckArchiveMessage: + title: PendingRegisterCheckArchiveMessage + type: object + description: SQS message containing a request to archive all data related to a register check + properties: + correlationId: + type: string + format: uuid + description: The id to allow the response from rca to be associated with the correct register status e.g. `VoterCardApplicationRegisterStatus.id` + example: c73bcdcc-2669-4bf6-81d3-e4ae73fb11fd + + required: + - correlationId + RegisterCheckResult: title: RegisterCheckResult description: Enum containing the possible values for a register check match outcome. @@ -350,3 +364,8 @@ components: application/json: schema: $ref: '#/components/schemas/RegisterCheckResultMessage' + PendingRegisterCheckArchiveMessage: + content: + application/json: + schema: + $ref: '#/components/schemas/PendingRegisterCheckArchiveMessage' diff --git a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/IntegrationTest.kt b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/IntegrationTest.kt index d7c60665..cfa3b58f 100644 --- a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/IntegrationTest.kt +++ b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/IntegrationTest.kt @@ -82,6 +82,9 @@ internal abstract class IntegrationTest { @Value("\${sqs.remove-applicant-register-check-data-queue-name}") protected lateinit var removeApplicantRegisterCheckDataQueueName: String + @Value("\${sqs.pending-register-check-archive-queue-name}") + protected lateinit var pendingRegisterCheckArchiveQueueName: String + @Value("\${caching.time-to-live}") protected lateinit var timeToLive: Duration diff --git a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/LocalStackContainerConfiguration.kt b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/LocalStackContainerConfiguration.kt index 28eaf033..1b750841 100644 --- a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/LocalStackContainerConfiguration.kt +++ b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/LocalStackContainerConfiguration.kt @@ -99,6 +99,7 @@ class LocalStackContainerConfiguration { @Value("\${sqs.overseas-vote-confirm-applicant-register-check-result-queue-name}") overseasVoteConfirmRegisterCheckResultMessageQueueName: String, @Value("\${sqs.register-check-result-response-queue-name}") registerCheckResultResponseQueueName: String, @Value("\${sqs.remove-applicant-register-check-data-queue-name}") removeRegisterCheckDataMessageQueueName: String, + @Value("\${sqs.pending-register-check-archive-queue-name}") pendingRegisterCheckArchiveQueueName: String, objectMapper: ObjectMapper, ): LocalStackContainerSettings { val queueUrlInitiateApplicantRegisterCheck = localStackContainer.createSqsQueue(initiateApplicantRegisterCheckQueueName, objectMapper) @@ -108,6 +109,7 @@ class LocalStackContainerConfiguration { val queueUrlOverseasVoteConfirmRegisterCheckResult = localStackContainer.createSqsQueue(overseasVoteConfirmRegisterCheckResultMessageQueueName, objectMapper) val queueUrlRegisterCheckResultResponse = localStackContainer.createSqsQueue(registerCheckResultResponseQueueName, objectMapper) val queueUrlRemoveRegisterCheckData = localStackContainer.createSqsQueue(removeRegisterCheckDataMessageQueueName, objectMapper) + val queueUrlPendingRegisterCheckArchive = localStackContainer.createSqsQueue(pendingRegisterCheckArchiveQueueName, objectMapper) val apiUrl = "http://${localStackContainer.host}:${localStackContainer.getMappedPort(DEFAULT_PORT)}" @@ -121,7 +123,8 @@ class LocalStackContainerConfiguration { queueUrlProxyVoteConfirmRegisterCheckResult = queueUrlProxyVoteConfirmRegisterCheckResult, queueUrlOverseasVoteConfirmRegisterCheckResult = queueUrlOverseasVoteConfirmRegisterCheckResult, queueUrlRemoveRegisterCheckData = queueUrlRemoveRegisterCheckData, - queueUrlRegisterCheckResultResponse = queueUrlRegisterCheckResultResponse + queueUrlRegisterCheckResultResponse = queueUrlRegisterCheckResultResponse, + queueUrlPendingRegisterCheckArchive = queueUrlPendingRegisterCheckArchive, ) } diff --git a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/LocalStackContainerSettings.kt b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/LocalStackContainerSettings.kt index 21d124db..b1921027 100644 --- a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/LocalStackContainerSettings.kt +++ b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/config/LocalStackContainerSettings.kt @@ -10,7 +10,8 @@ data class LocalStackContainerSettings( val queueUrlProxyVoteConfirmRegisterCheckResult: String, val queueUrlOverseasVoteConfirmRegisterCheckResult: String, val queueUrlRemoveRegisterCheckData: String, - val queueUrlRegisterCheckResultResponse: String + val queueUrlRegisterCheckResultResponse: String, + val queueUrlPendingRegisterCheckArchive: String, ) { val mappedQueueUrlInitiateApplicantRegisterCheck: String = toMappedUrl(queueUrlInitiateApplicantRegisterCheck, apiUrl) val mappedQueueUrlConfirmRegisterCheckResult: String = toMappedUrl(queueUrlConfirmRegisterCheckResult, apiUrl) @@ -19,6 +20,7 @@ data class LocalStackContainerSettings( val mappedQueueUrlOverseasVoteConfirmRegisterCheckResult: String = toMappedUrl(queueUrlOverseasVoteConfirmRegisterCheckResult, apiUrl) val mappedQueueUrlRegisterCheckResultResponse: String = toMappedUrl(queueUrlRegisterCheckResultResponse, apiUrl) val mappedQueueUrlRemoveRegisterCheckData: String = toMappedUrl(queueUrlRemoveRegisterCheckData, apiUrl) + val mappedQueueUrlPendingRegisterCheckArchive: String = toMappedUrl(queueUrlPendingRegisterCheckArchive, apiUrl) val sesMessagesUrl = "$apiUrl/_aws/ses" private fun toMappedUrl(rawUrlString: String, apiUrlString: String): String { diff --git a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/logging/CorrelationIdMdcIntegrationTest.kt b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/logging/CorrelationIdMdcIntegrationTest.kt index b2fcf35a..f125083d 100644 --- a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/logging/CorrelationIdMdcIntegrationTest.kt +++ b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/logging/CorrelationIdMdcIntegrationTest.kt @@ -190,6 +190,7 @@ internal class CorrelationIdMdcIntegrationTest : IntegrationTest() { fun deletePostalRecordsBefore() { ClearDownUtils.clearDownRecords( postalRepository = postalVoteApplicationRepository, + registerCheckRepository = registerCheckRepository, sqsAsyncClient = sqsAsyncClient, queueName = postalApplicationQueueName ) @@ -199,6 +200,7 @@ internal class CorrelationIdMdcIntegrationTest : IntegrationTest() { fun deletePostalRecordsAfter() { ClearDownUtils.clearDownRecords( postalRepository = postalVoteApplicationRepository, + registerCheckRepository = registerCheckRepository, sqsAsyncClient = sqsAsyncClient, queueName = postalApplicationQueueName ) diff --git a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/IntegrationDataRemovalIntegrationTest.kt b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/IntegrationDataRemovalIntegrationTest.kt index d7787e50..af28629c 100644 --- a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/IntegrationDataRemovalIntegrationTest.kt +++ b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/IntegrationDataRemovalIntegrationTest.kt @@ -50,6 +50,7 @@ private class IntegrationDataRemovalIntegrationTest : IntegrationTest() { fun deletePostalRecordsBefore() { ClearDownUtils.clearDownRecords( postalRepository = postalVoteApplicationRepository, + registerCheckRepository = registerCheckRepository, queueName = removeApplicationEmsDataQueueName ) } @@ -58,6 +59,7 @@ private class IntegrationDataRemovalIntegrationTest : IntegrationTest() { fun deletePostalRecordsAfter() { ClearDownUtils.clearDownRecords( postalRepository = postalVoteApplicationRepository, + registerCheckRepository = registerCheckRepository, queueName = removeApplicationEmsDataQueueName ) } diff --git a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/MessageQueueResolverTest.kt b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/MessageQueueResolverTest.kt index 879fcc08..4441abc2 100644 --- a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/MessageQueueResolverTest.kt +++ b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/MessageQueueResolverTest.kt @@ -8,6 +8,7 @@ import org.junit.jupiter.api.extension.ExtendWith import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import uk.gov.dluhc.messagingsupport.MessageQueue +import uk.gov.dluhc.registercheckerapi.messaging.models.PendingRegisterCheckArchiveMessage import uk.gov.dluhc.registercheckerapi.messaging.models.RegisterCheckResultMessage import uk.gov.dluhc.registercheckerapi.messaging.models.SourceType @@ -29,6 +30,9 @@ internal class MessageQueueResolverTest { @Mock private lateinit var registerCheckResultResponseQueue: MessageQueue + @Mock + private lateinit var pendingRegisterCheckArchiveQueue: MessageQueue + private lateinit var messageQueueResolver: MessageQueueResolver @BeforeEach @@ -39,8 +43,8 @@ internal class MessageQueueResolverTest { proxyVoteConfirmRegisterCheckResultQueue = proxyVoteConfirmRegisterCheckResultMockQueue, overseasVoteConfirmRegisterCheckResultQueue = overseasVoteConfirmRegisterCheckResultMockQueue, confirmRegisterCheckResultQueue = confirmRegisterCheckResultMockQueue, - registerCheckResultResponseQueue = registerCheckResultResponseQueue - + registerCheckResultResponseQueue = registerCheckResultResponseQueue, + pendingRegisterCheckArchiveQueue = pendingRegisterCheckArchiveQueue, ) } diff --git a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/PendingRegisterCheckArchiveIntegrationTest.kt b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/PendingRegisterCheckArchiveIntegrationTest.kt new file mode 100644 index 00000000..6c10ac8d --- /dev/null +++ b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/messaging/PendingRegisterCheckArchiveIntegrationTest.kt @@ -0,0 +1,135 @@ +package uk.gov.dluhc.emsintegrationapi.messaging + +import ch.qos.logback.classic.Level +import io.awspring.cloud.sqs.operations.SqsSendOptions +import org.assertj.core.api.Assertions.assertThat +import org.awaitility.kotlin.await +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import uk.gov.dluhc.emsintegrationapi.config.IntegrationTest +import uk.gov.dluhc.emsintegrationapi.database.entity.CheckStatus +import uk.gov.dluhc.emsintegrationapi.testsupport.ClearDownUtils +import uk.gov.dluhc.emsintegrationapi.testsupport.TestLogAppender +import uk.gov.dluhc.emsintegrationapi.testsupport.testdata.entity.buildRegisterCheck +import uk.gov.dluhc.registercheckerapi.messaging.models.PendingRegisterCheckArchiveMessage +import java.time.Instant +import java.util.UUID +import java.util.concurrent.TimeUnit + +internal class PendingRegisterCheckArchiveIntegrationTest : IntegrationTest() { + @BeforeEach + fun deletePostalRecordsBefore() { + ClearDownUtils.clearDownRecords( + registerCheckRepository = registerCheckRepository, + queueName = pendingRegisterCheckArchiveQueueName, + ) + } + + @AfterEach + fun deletePostalRecordsAfter() { + ClearDownUtils.clearDownRecords( + registerCheckRepository = registerCheckRepository, + queueName = pendingRegisterCheckArchiveQueueName, + ) + } + + @Test + fun `should not archive expired register check`() { + val correlationId = UUID.randomUUID() + val registerCheck = + buildRegisterCheck( + correlationId = correlationId, + status = CheckStatus.EXPIRED, + ) + + registerCheckRepository.save(registerCheck) + + val payload = + PendingRegisterCheckArchiveMessage( + correlationId = correlationId, + ) + + sqsMessagingTemplate.send( + { to: SqsSendOptions -> + to + .queue(pendingRegisterCheckArchiveQueueName) + .payload(payload) + }, + ) + + await.atMost(10, TimeUnit.SECONDS).untilAsserted { + val registerCheck1 = registerCheckRepository.findByCorrelationId(correlationId) + assertThat(registerCheck1).isNotNull + assertThat(registerCheck1?.status).isEqualTo(CheckStatus.EXPIRED) + assertThat( + TestLogAppender.hasLog( + "Register Check with correlationId $correlationId has status ${CheckStatus.EXPIRED} so cannot be archived (must be at status PENDING)", + Level.WARN, + ), + ).isTrue() + } + } + + @Test + fun `should not archive pending register check when not found`() { + val correlationId = UUID.randomUUID() + + val payload = + PendingRegisterCheckArchiveMessage( + correlationId = correlationId, + ) + + sqsMessagingTemplate.send( + { to: SqsSendOptions -> + to + .queue(pendingRegisterCheckArchiveQueueName) + .payload(payload) + }, + ) + + await.atMost(10, TimeUnit.SECONDS).untilAsserted { + val registerCheck1 = registerCheckRepository.findByCorrelationId(correlationId) + assertThat(registerCheck1).isNull() + assertThat( + TestLogAppender.hasLog( + "Pending register check for requestid:[$correlationId] not found", + Level.WARN, + ), + ).isTrue() + } + } + + @Test + fun `should archive pending register check when found`() { + val correlationId = UUID.randomUUID() + val registerCheck = + buildRegisterCheck( + correlationId = correlationId, + status = CheckStatus.PENDING, + ) + val checkInstant = Instant.now() + + registerCheckRepository.save(registerCheck) + + val payload = + PendingRegisterCheckArchiveMessage( + correlationId = correlationId, + ) + + sqsMessagingTemplate.send( + { to: SqsSendOptions -> + to + .queue(pendingRegisterCheckArchiveQueueName) + .payload(payload) + }, + ) + + await.atMost(10, TimeUnit.SECONDS).untilAsserted { + val registerCheck1 = registerCheckRepository.findByCorrelationId(correlationId) + assertThat(registerCheck1).isNotNull + assertThat(registerCheck1?.status).isEqualTo(CheckStatus.ARCHIVED) + assertThat(registerCheck1?.matchResultSentAt?.isAfter(checkInstant)) + } + } +} diff --git a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/service/PendingPendingRegisterCheckArchiveServiceTest.kt b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/service/PendingPendingRegisterCheckArchiveServiceTest.kt new file mode 100644 index 00000000..bb6d943b --- /dev/null +++ b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/service/PendingPendingRegisterCheckArchiveServiceTest.kt @@ -0,0 +1,115 @@ +package uk.gov.dluhc.emsintegrationapi.service + +import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.api.Assertions.catchThrowableOfType +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.ArgumentMatchers.any +import org.mockito.BDDMockito.given +import org.mockito.InjectMocks +import org.mockito.Mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.junit.jupiter.MockitoExtension +import uk.gov.dluhc.emsintegrationapi.database.entity.CheckStatus +import uk.gov.dluhc.emsintegrationapi.database.repository.RegisterCheckRepository +import uk.gov.dluhc.emsintegrationapi.exception.OptimisticLockingFailureException +import uk.gov.dluhc.emsintegrationapi.exception.PendingRegisterCheckArchiveInvalidStatusException +import uk.gov.dluhc.emsintegrationapi.exception.PendingRegisterCheckNotFoundException +import uk.gov.dluhc.emsintegrationapi.testsupport.testdata.entity.buildRegisterCheck +import java.util.UUID + +@ExtendWith(MockitoExtension::class) +internal class PendingPendingRegisterCheckArchiveServiceTest { + @Mock + private lateinit var registerCheckRepository: RegisterCheckRepository + + @InjectMocks + private lateinit var pendingRegisterCheckArchiveService: PendingRegisterCheckArchiveService + + @Nested + inner class ArchiveRegisterCheckData { + @Test + fun `should handle null correlation ID`() { + val ex = + catchThrowableOfType( + { pendingRegisterCheckArchiveService.archiveIfStatusIsPending(null) }, + IllegalArgumentException::class.java, + ) + assertThat(ex.message).isEqualTo("Correlation ID is null") + } + + @Test + fun `should not archive any records for a non-existing register check`() { + // Given + val correlationId = UUID.randomUUID() + val expected = PendingRegisterCheckNotFoundException(correlationId) + + // When + val ex = + catchThrowableOfType( + { pendingRegisterCheckArchiveService.archiveIfStatusIsPending(correlationId) }, + PendingRegisterCheckNotFoundException::class.java, + ) + + // Then + assertThat(ex).message().isEqualTo(expected.message) + } + + @Test + fun `should not archive any records for an already archived register check`() { + // Given + val correlationId = UUID.randomUUID() + val registerCheck = buildRegisterCheck(correlationId = correlationId, status = CheckStatus.ARCHIVED) + given(registerCheckRepository.findByCorrelationId(correlationId)).willReturn(registerCheck) + val expected = PendingRegisterCheckArchiveInvalidStatusException(CheckStatus.ARCHIVED) + + // When + val ex = + catchThrowableOfType( + { pendingRegisterCheckArchiveService.archiveIfStatusIsPending(correlationId) }, + PendingRegisterCheckArchiveInvalidStatusException::class.java, + ) + + // Then + verify(registerCheckRepository).findByCorrelationId(correlationId) + verify(registerCheckRepository, never()).save(registerCheck) + assertThat(ex).message().isEqualTo(expected.message) + } + + @Test + fun `should archive record for a pending register check`() { + // Given + val correlationId = UUID.randomUUID() + val registerCheck = buildRegisterCheck(correlationId = correlationId, status = CheckStatus.PENDING) + given(registerCheckRepository.findByCorrelationId(correlationId)).willReturn(registerCheck) + val expected = PendingRegisterCheckArchiveInvalidStatusException(CheckStatus.ARCHIVED) + + // When + pendingRegisterCheckArchiveService.archiveIfStatusIsPending(correlationId) + + // Then + verify(registerCheckRepository).findByCorrelationId(correlationId) + verify(registerCheckRepository).save(registerCheck) + } + + @Test + fun `should handle optimistic locking exception encountered on register check repository save operation`() { + // Given + val correlationId = UUID.randomUUID() + val registerCheck = buildRegisterCheck(correlationId = correlationId, status = CheckStatus.PENDING) + given(registerCheckRepository.findByCorrelationId(correlationId)).willReturn(registerCheck) + given(registerCheckRepository.save(any())).willThrow(OptimisticLockingFailureException(correlationId = correlationId)) + val ex = + catchThrowableOfType( + { pendingRegisterCheckArchiveService.archiveIfStatusIsPending(correlationId) }, + OptimisticLockingFailureException::class.java, + ) + assertThat(ex.message).isEqualTo("Register check with requestid:[$correlationId] has an optimistic locking failure") + verify(registerCheckRepository).findByCorrelationId(correlationId) + // save call was made although it threw exception + verify(registerCheckRepository).save(registerCheck) + } + } +} diff --git a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/testsupport/ClearDownUtils.kt b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/testsupport/ClearDownUtils.kt index a0f2aa1b..5218fc86 100644 --- a/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/testsupport/ClearDownUtils.kt +++ b/src/test/kotlin/uk/gov/dluhc/emsintegrationapi/testsupport/ClearDownUtils.kt @@ -4,17 +4,20 @@ import software.amazon.awssdk.services.sqs.SqsAsyncClient import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest import uk.gov.dluhc.emsintegrationapi.database.repository.PostalVoteApplicationRepository import uk.gov.dluhc.emsintegrationapi.database.repository.ProxyVoteApplicationRepository +import uk.gov.dluhc.emsintegrationapi.database.repository.RegisterCheckRepository object ClearDownUtils { fun clearDownRecords( proxyRepository: ProxyVoteApplicationRepository? = null, postalRepository: PostalVoteApplicationRepository? = null, + registerCheckRepository: RegisterCheckRepository? = null, sqsAsyncClient: SqsAsyncClient? = null, queueName: String? = null ) { proxyRepository?.deleteAll() postalRepository?.deleteAll() + registerCheckRepository?.deleteAll() queueName?.let { val request = PurgeQueueRequest.builder().queueUrl(queueName).build() sqsAsyncClient?.purgeQueue(request) diff --git a/src/test/resources/application-integration-test.yml b/src/test/resources/application-integration-test.yml index c27b24d7..eef718e5 100644 --- a/src/test/resources/application-integration-test.yml +++ b/src/test/resources/application-integration-test.yml @@ -52,6 +52,7 @@ sqs: proxy-vote-confirm-applicant-register-check-result-queue-name: proxy-vote-confirm-applicant-register-check-result overseas-vote-confirm-applicant-register-check-result-queue-name: overseas-vote-confirm-applicant-register-check-result register-check-result-response-queue-name: register-check-result-response-queue-name + pending-register-check-archive-queue-name: pending-register-check-archive-queue-name api: ero-management: