diff --git a/build.gradle.kts b/build.gradle.kts
index 89df274..78bad77 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -62,6 +62,7 @@ dependencies {
providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.projectreactor:reactor-test")
+ testImplementation("org.mockito.kotlin:mockito-kotlin:5.0.0")
integrationTestImplementation("org.testcontainers:junit-jupiter")
integrationTestImplementation("org.testcontainers:postgresql")
}
diff --git a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt
index e04e568..fcb0863 100644
--- a/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt
+++ b/src/main/kotlin/dev/dnpm/etl/processor/services/RequestProcessor.kt
@@ -49,15 +49,7 @@ class RequestProcessor(
val pid = mtbFile.patient.id
val pseudonymized = pseudonymizeService.pseudonymize(mtbFile)
- val allRequests = requestService.allRequestsByPatientPseudonym(pseudonymized.patient.id)
-
- val lastMtbFileRequestForPatient = allRequests
- .filter { it.type == RequestType.MTB_FILE }
- .firstOrNull { it.status == RequestStatus.SUCCESS || it.status == RequestStatus.WARNING }
-
- val isLastRequestDeletion = allRequests.firstOrNull()?.type == RequestType.DELETE
-
- if (null != lastMtbFileRequestForPatient && lastMtbFileRequestForPatient.fingerprint == fingerprint(mtbFile) && !isLastRequestDeletion) {
+ if (isDuplication(pseudonymized)) {
requestService.save(
Request(
patientId = pseudonymized.patient.id,
@@ -124,6 +116,16 @@ class RequestProcessor(
statisticsUpdateProducer.emitNext("", Sinks.EmitFailureHandler.FAIL_FAST)
}
+ private fun isDuplication(pseudonymizedMtbFile: MtbFile): Boolean {
+ val lastMtbFileRequestForPatient =
+ requestService.lastMtbFileRequestForPatientPseudonym(pseudonymizedMtbFile.patient.id)
+ val isLastRequestDeletion = requestService.isLastRequestDeletion(pseudonymizedMtbFile.patient.id)
+
+ return null != lastMtbFileRequestForPatient
+ && !isLastRequestDeletion
+ && lastMtbFileRequestForPatient.fingerprint == fingerprint(pseudonymizedMtbFile)
+ }
+
fun processDeletion(patientId: String) {
val requestId = UUID.randomUUID().toString()
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt
new file mode 100644
index 0000000..c165cf0
--- /dev/null
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt
@@ -0,0 +1,209 @@
+/*
+ * This file is part of ETL-Processor
+ *
+ * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken, Datenintegrationszentrum Philipps-Universität Marburg and Contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package dev.dnpm.etl.processor.services
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import de.ukw.ccc.bwhc.dto.*
+import dev.dnpm.etl.processor.monitoring.Request
+import dev.dnpm.etl.processor.monitoring.RequestStatus
+import dev.dnpm.etl.processor.monitoring.RequestType
+import dev.dnpm.etl.processor.output.MtbFileSender
+import dev.dnpm.etl.processor.output.RestMtbFileSender
+import dev.dnpm.etl.processor.pseudonym.PseudonymizeService
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito.*
+import org.mockito.junit.jupiter.MockitoExtension
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import reactor.core.publisher.Sinks
+import java.time.Instant
+import java.util.*
+
+
+@ExtendWith(MockitoExtension::class)
+class RequestProcessorTest {
+
+ private lateinit var pseudonymizeService: PseudonymizeService
+ private lateinit var sender: MtbFileSender
+ private lateinit var requestService: RequestService
+ private lateinit var statisticsUpdateProducer: Sinks.Many
+
+ private lateinit var requestProcessor: RequestProcessor
+
+ @BeforeEach
+ fun setup(
+ @Mock pseudonymizeService: PseudonymizeService,
+ @Mock sender: RestMtbFileSender,
+ @Mock requestService: RequestService,
+ ) {
+ this.pseudonymizeService = pseudonymizeService
+ this.sender = sender
+ this.requestService = requestService
+ this.statisticsUpdateProducer = Sinks.many().multicast().directBestEffort()
+
+ val objectMapper = ObjectMapper()
+
+ requestProcessor = RequestProcessor(
+ pseudonymizeService,
+ listOf(sender),
+ requestService,
+ objectMapper,
+ statisticsUpdateProducer
+ )
+ }
+
+ @Test
+ fun testShouldDetectMtbFileDuplicationAndSaveRequestStatus() {
+ doAnswer {
+ Request(
+ id = 1L,
+ uuid = UUID.randomUUID().toString(),
+ patientId = "TEST_12345678901",
+ pid = "P1",
+ fingerprint = "cwaxsvectyfj4qcw4hiwzx5fwwo7lekyagpzd2ayuf36jlvi6msa",
+ type = RequestType.MTB_FILE,
+ status = RequestStatus.SUCCESS,
+ processedAt = Instant.parse("2023-08-08T02:00:00Z")
+ )
+ }.`when`(requestService).lastMtbFileRequestForPatientPseudonym(anyString())
+
+ doAnswer {
+ false
+ }.`when`(requestService).isLastRequestDeletion(anyString())
+
+ doAnswer {
+ it.arguments[0] as MtbFile
+ }.`when`(pseudonymizeService).pseudonymize(any())
+
+ val mtbFile = MtbFile.builder()
+ .withPatient(
+ Patient.builder()
+ .withId("1")
+ .withBirthDate("2000-08-08")
+ .withGender(Patient.Gender.MALE)
+ .build()
+ )
+ .withConsent(
+ Consent.builder()
+ .withId("1")
+ .withStatus(Consent.Status.ACTIVE)
+ .withPatient("123")
+ .build()
+ )
+ .withEpisode(
+ Episode.builder()
+ .withId("1")
+ .withPatient("1")
+ .withPeriod(PeriodStart("2023-08-08"))
+ .build()
+ )
+ .build()
+
+ this.requestProcessor.processMtbFile(mtbFile)
+
+ val requestCaptor = argumentCaptor()
+ verify(requestService, times(1)).save(requestCaptor.capture())
+ assertThat(requestCaptor.firstValue).isNotNull
+ assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.DUPLICATION)
+ }
+
+ @Test
+ fun testShouldSendMtbFileAndSaveRequestStatus() {
+ doAnswer {
+ Request(
+ id = 1L,
+ uuid = UUID.randomUUID().toString(),
+ patientId = "TEST_12345678901",
+ pid = "P1",
+ fingerprint = "different",
+ type = RequestType.MTB_FILE,
+ status = RequestStatus.SUCCESS,
+ processedAt = Instant.parse("2023-08-08T02:00:00Z")
+ )
+ }.`when`(requestService).lastMtbFileRequestForPatientPseudonym(anyString())
+
+ doAnswer {
+ false
+ }.`when`(requestService).isLastRequestDeletion(anyString())
+
+ doAnswer {
+ MtbFileSender.Response(status = MtbFileSender.ResponseStatus.SUCCESS)
+ }.`when`(sender).send(any())
+
+ doAnswer {
+ it.arguments[0] as MtbFile
+ }.`when`(pseudonymizeService).pseudonymize(any())
+
+ val mtbFile = MtbFile.builder()
+ .withPatient(
+ Patient.builder()
+ .withId("1")
+ .withBirthDate("2000-08-08")
+ .withGender(Patient.Gender.MALE)
+ .build()
+ )
+ .withConsent(
+ Consent.builder()
+ .withId("1")
+ .withStatus(Consent.Status.ACTIVE)
+ .withPatient("123")
+ .build()
+ )
+ .withEpisode(
+ Episode.builder()
+ .withId("1")
+ .withPatient("1")
+ .withPeriod(PeriodStart("2023-08-08"))
+ .build()
+ )
+ .build()
+
+ this.requestProcessor.processMtbFile(mtbFile)
+
+ val requestCaptor = argumentCaptor()
+ verify(requestService, times(1)).save(requestCaptor.capture())
+ assertThat(requestCaptor.firstValue).isNotNull
+ assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS)
+ }
+
+ @Test
+ fun testShouldSendDeleterequestAndSaveRequestStatus() {
+ doAnswer {
+ "PSEUDONYM"
+ }.`when`(pseudonymizeService).patientPseudonym(anyString())
+
+ doAnswer {
+ MtbFileSender.Response(status = MtbFileSender.ResponseStatus.SUCCESS)
+ }.`when`(sender).send(any())
+
+ this.requestProcessor.processDeletion("TEST_12345678901")
+
+ val requestCaptor = argumentCaptor()
+ verify(requestService, times(1)).save(requestCaptor.capture())
+ assertThat(requestCaptor.firstValue).isNotNull
+ assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS)
+ }
+
+}
\ No newline at end of file