summaryrefslogtreecommitdiff
path: root/src/test/kotlin/dev
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/kotlin/dev')
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt19
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt149
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt47
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt171
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt31
-rw-r--r--src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt68
6 files changed, 448 insertions, 37 deletions
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt
index f2abd27..fbcfb3f 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
import de.ukw.ccc.bwhc.dto.Consent
import de.ukw.ccc.bwhc.dto.MtbFile
import de.ukw.ccc.bwhc.dto.Patient
+import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.CustomMediaType
import dev.dnpm.etl.processor.services.RequestProcessor
import org.apache.kafka.clients.consumer.ConsumerRecord
@@ -34,10 +35,7 @@ import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.Mock
import org.mockito.junit.jupiter.MockitoExtension
-import org.mockito.kotlin.any
-import org.mockito.kotlin.anyValueClass
-import org.mockito.kotlin.times
-import org.mockito.kotlin.verify
+import org.mockito.kotlin.*
import java.util.*
@ExtendWith(MockitoExtension::class)
@@ -49,7 +47,7 @@ class KafkaInputListenerTest {
@BeforeEach
fun setup(
- @Mock requestProcessor: RequestProcessor
+ @Mock requestProcessor: RequestProcessor,
) {
this.requestProcessor = requestProcessor
this.objectMapper = ObjectMapper()
@@ -94,7 +92,10 @@ class KafkaInputListenerTest {
)
)
- verify(requestProcessor, times(1)).processDeletion(anyValueClass())
+ verify(requestProcessor, times(1)).processDeletion(
+ anyValueClass(),
+ eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)
+ )
}
@Test
@@ -147,7 +148,8 @@ class KafkaInputListenerTest {
Optional.empty()
)
)
- verify(requestProcessor, times(1)).processDeletion(anyValueClass(), anyValueClass())
+ verify(requestProcessor, times(1)).processDeletion(anyValueClass(), anyValueClass(), eq(
+ TtpConsentStatus.UNKNOWN_CHECK_FILE))
}
@Test
@@ -178,7 +180,8 @@ class KafkaInputListenerTest {
Optional.empty()
)
)
- verify(requestProcessor, times(0)).processDeletion(anyValueClass(), anyValueClass())
+ verify(requestProcessor, times(0)).processDeletion(anyValueClass(), anyValueClass(), eq(
+ TtpConsentStatus.UNKNOWN_CHECK_FILE))
}
}
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
index 4a33078..eb7e0b6 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt
@@ -21,21 +21,29 @@ package dev.dnpm.etl.processor.input
import com.fasterxml.jackson.databind.ObjectMapper
import de.ukw.ccc.bwhc.dto.*
+import de.ukw.ccc.bwhc.dto.Consent.Status
import dev.dnpm.etl.processor.CustomMediaType
+import dev.dnpm.etl.processor.consent.ConsentByMtbFile
+import dev.dnpm.etl.processor.consent.GicsConsentService
+import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.services.RequestProcessor
import dev.pcvolkmer.mv64e.mtb.Mtb
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
import org.mockito.Mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.anyValueClass
+import org.mockito.kotlin.whenever
import org.springframework.core.io.ClassPathResource
import org.springframework.http.MediaType
+import org.springframework.test.context.TestPropertySource
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.delete
import org.springframework.test.web.servlet.post
@@ -53,19 +61,22 @@ class MtbFileRestControllerTest {
private lateinit var requestProcessor: RequestProcessor
+
@BeforeEach
fun setup(
@Mock requestProcessor: RequestProcessor
) {
this.requestProcessor = requestProcessor
- val controller = MtbFileRestController(requestProcessor)
+ val controller = MtbFileRestController(requestProcessor,
+ ConsentByMtbFile()
+ )
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
}
@Test
fun shouldProcessPostRequest() {
mockMvc.post("/mtbfile") {
- content = objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.ACTIVE))
+ content = objectMapper.writeValueAsString(bwhcMtbFileContent(Status.ACTIVE))
contentType = MediaType.APPLICATION_JSON
}.andExpect {
status {
@@ -79,7 +90,70 @@ class MtbFileRestControllerTest {
@Test
fun shouldProcessPostRequestWithRejectedConsent() {
mockMvc.post("/mtbfile") {
- content = objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.REJECTED))
+ content =
+ objectMapper.writeValueAsString(bwhcMtbFileContent(Status.REJECTED))
+ contentType = MediaType.APPLICATION_JSON
+ }.andExpect {
+ status {
+ isAccepted()
+ }
+ }
+
+ verify(requestProcessor, times(1)).processDeletion(
+ anyValueClass(),
+ org.mockito.kotlin.eq(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED)
+ )
+ }
+
+ @Test
+ fun shouldProcessDeleteRequest() {
+ mockMvc.delete("/mtbfile/TEST_12345678").andExpect {
+ status {
+ isAccepted()
+ }
+ }
+
+ verify(requestProcessor, times(1)).processDeletion(
+ anyValueClass(),
+ org.mockito.kotlin.eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)
+ )
+ }
+ }
+
+ @TestPropertySource(
+ properties = ["app.consent.gics.enabled=true",
+ "app.consent.gics.gIcsBaseUri=http://localhost:8090/ttp-fhir/fhir/gics"]
+ )
+ @Nested
+ inner class BwhcRequestsCheckConsentViaTtp {
+
+ private lateinit var mockMvc: MockMvc
+
+ private lateinit var requestProcessor: RequestProcessor
+
+ private lateinit var gicsConsentService: GicsConsentService
+
+ @BeforeEach
+ fun setup(
+ @Mock requestProcessor: RequestProcessor,
+ @Mock gicsConsentService: GicsConsentService
+ ) {
+ this.requestProcessor = requestProcessor
+ val controller = MtbFileRestController(requestProcessor, gicsConsentService)
+ this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
+ this.gicsConsentService = gicsConsentService
+
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = ["ACTIVE", "REJECTED"])
+ fun shouldProcessPostRequest(status: String) {
+
+ whenever(gicsConsentService.getTtpBroadConsentStatus(any())).thenReturn(TtpConsentStatus.BROAD_CONSENT_GIVEN)
+
+ mockMvc.post("/mtbfile") {
+ content =
+ objectMapper.writeValueAsString(bwhcMtbFileContent(Status.valueOf(status)))
contentType = MediaType.APPLICATION_JSON
}.andExpect {
status {
@@ -87,21 +161,52 @@ class MtbFileRestControllerTest {
}
}
- verify(requestProcessor, times(1)).processDeletion(anyValueClass())
+ verify(requestProcessor, times(1)).processMtbFile(any<MtbFile>())
+ }
+
+
+ @ParameterizedTest
+ @ValueSource(strings = ["ACTIVE", "REJECTED"])
+ fun shouldProcessPostRequestWithRejectedConsent(status: String) {
+
+ whenever(gicsConsentService.getTtpBroadConsentStatus(any())).thenReturn(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED)
+
+ mockMvc.post("/mtbfile") {
+ content =
+ objectMapper.writeValueAsString(bwhcMtbFileContent(Status.valueOf(status)))
+ contentType = MediaType.APPLICATION_JSON
+ }.andExpect {
+ status {
+ isAccepted()
+ }
+ }
+
+ // consent status from ttp should override file consent value
+ verify(requestProcessor, times(1)).processDeletion(
+ anyValueClass(),
+ org.mockito.kotlin.eq(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED)
+ )
}
@Test
fun shouldProcessDeleteRequest() {
+
mockMvc.delete("/mtbfile/TEST_12345678").andExpect {
status {
isAccepted()
}
}
- verify(requestProcessor, times(1)).processDeletion(anyValueClass())
+ verify(requestProcessor, times(1)).processDeletion(
+ anyValueClass(),
+ org.mockito.kotlin.eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)
+ )
+ verify(gicsConsentService, times(0)).getTtpBroadConsentStatus(any())
+
}
}
+
@Nested
inner class BwhcRequestsWithAlias {
@@ -114,14 +219,16 @@ class MtbFileRestControllerTest {
@Mock requestProcessor: RequestProcessor
) {
this.requestProcessor = requestProcessor
- val controller = MtbFileRestController(requestProcessor)
+ val controller = MtbFileRestController(requestProcessor,
+ ConsentByMtbFile()
+ )
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
}
@Test
fun shouldProcessPostRequest() {
mockMvc.post("/mtb") {
- content = objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.ACTIVE))
+ content = objectMapper.writeValueAsString(bwhcMtbFileContent(Status.ACTIVE))
contentType = MediaType.APPLICATION_JSON
}.andExpect {
status {
@@ -135,7 +242,8 @@ class MtbFileRestControllerTest {
@Test
fun shouldProcessPostRequestWithRejectedConsent() {
mockMvc.post("/mtb") {
- content = objectMapper.writeValueAsString(bwhcMtbFileContent(Consent.Status.REJECTED))
+ content =
+ objectMapper.writeValueAsString(bwhcMtbFileContent(Status.REJECTED))
contentType = MediaType.APPLICATION_JSON
}.andExpect {
status {
@@ -143,7 +251,11 @@ class MtbFileRestControllerTest {
}
}
- verify(requestProcessor, times(1)).processDeletion(anyValueClass())
+ verify(requestProcessor, times(1)).processDeletion(
+ anyValueClass(), org.mockito.kotlin.eq(
+ TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED
+ )
+ )
}
@Test
@@ -154,7 +266,11 @@ class MtbFileRestControllerTest {
}
}
- verify(requestProcessor, times(1)).processDeletion(anyValueClass())
+ verify(requestProcessor, times(1)).processDeletion(
+ anyValueClass(), org.mockito.kotlin.eq(
+ TtpConsentStatus.UNKNOWN_CHECK_FILE
+ )
+ )
}
}
@@ -167,16 +283,21 @@ class MtbFileRestControllerTest {
@BeforeEach
fun setup(
- @Mock requestProcessor: RequestProcessor
+ @Mock requestProcessor: RequestProcessor,
+ @Mock gicsConsentService: GicsConsentService
) {
this.requestProcessor = requestProcessor
- val controller = MtbFileRestController(requestProcessor)
+ val controller = MtbFileRestController(requestProcessor,
+ gicsConsentService
+ )
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
}
@Test
fun shouldRespondPostRequest() {
- val mtbFileContent = ClassPathResource("mv64e-mtb-fake-patient.json").inputStream.readAllBytes().toString(Charsets.UTF_8)
+ val mtbFileContent =
+ ClassPathResource("mv64e-mtb-fake-patient.json").inputStream.readAllBytes()
+ .toString(Charsets.UTF_8)
mockMvc.post("/mtb") {
content = mtbFileContent
@@ -193,7 +314,7 @@ class MtbFileRestControllerTest {
}
companion object {
- fun bwhcMtbFileContent(consentStatus: Consent.Status) = MtbFile.builder()
+ fun bwhcMtbFileContent(consentStatus: Status) = MtbFile.builder()
.withPatient(
Patient.builder()
.withId("TEST_12345678")
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt
index 65986f1..b6baec9 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt
@@ -19,11 +19,19 @@
package dev.dnpm.etl.processor.pseudonym
+import ca.uhn.fhir.context.FhirContext
import com.fasterxml.jackson.databind.ObjectMapper
import de.ukw.ccc.bwhc.dto.*
import de.ukw.ccc.bwhc.dto.Patient
+import dev.dnpm.etl.processor.config.AppConfigProperties
+import dev.dnpm.etl.processor.config.GIcsConfigProperties
+import dev.dnpm.etl.processor.config.JacksonConfig
+import dev.dnpm.etl.processor.consent.ConsentByMtbFile
+import dev.dnpm.etl.processor.services.ConsentProcessor
+import dev.dnpm.etl.processor.services.ConsentProcessorTest
import dev.pcvolkmer.mv64e.mtb.*
import org.assertj.core.api.Assertions.assertThat
+import org.hl7.fhir.r4.model.Bundle
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
@@ -39,6 +47,9 @@ import java.util.*
@ExtendWith(MockitoExtension::class)
class ExtensionsTest {
+ fun getObjectMapper(): ObjectMapper {
+ return JacksonConfig().objectMapper()
+ }
@Nested
inner class UsingBwhcDatamodel {
@@ -46,13 +57,14 @@ class ExtensionsTest {
val FAKE_MTB_FILE_PATH = "fake_MTBFile.json"
val CLEAN_PATIENT_ID = "5dad2f0b-49c6-47d8-a952-7b9e9e0f7549"
+
private fun fakeMtbFile(): MtbFile {
val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream
- return ObjectMapper().readValue(mtbFile, MtbFile::class.java)
+ return getObjectMapper().readValue(mtbFile, MtbFile::class.java)
}
private fun MtbFile.serialized(): String {
- return ObjectMapper().writeValueAsString(this)
+ return getObjectMapper().writeValueAsString(this)
}
@Test
@@ -86,7 +98,9 @@ class ExtensionsTest {
mtbFile.pseudonymizeWith(pseudonymizeService)
mtbFile.anonymizeContentWith(pseudonymizeService)
- val pattern = "\"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\"".toRegex().toPattern()
+ val pattern =
+ "\"[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}\"".toRegex()
+ .toPattern()
val matcher = pattern.matcher(mtbFile.serialized())
assertThrows<IllegalStateException> {
@@ -207,15 +221,15 @@ class ExtensionsTest {
inner class UsingDnpmV2Datamodel {
val FAKE_MTB_FILE_PATH = "mv64e-mtb-fake-patient.json"
- val CLEAN_PATIENT_ID = "aca5a971-28be-4089-8128-0036a4fe6f1a"
+ val CLEAN_PATIENT_ID = "644bae7a-56f6-4ee8-b02f-c532e65af5b1"
private fun fakeMtbFile(): Mtb {
val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream
- return ObjectMapper().readValue(mtbFile, Mtb::class.java)
+ return getObjectMapper().readValue(mtbFile, Mtb::class.java)
}
private fun Mtb.serialized(): String {
- return ObjectMapper().writeValueAsString(this)
+ return getObjectMapper().writeValueAsString(this)
}
@Test
@@ -226,6 +240,8 @@ class ExtensionsTest {
}.whenever(pseudonymizeService).patientPseudonym(anyValueClass())
val mtbFile = fakeMtbFile()
+ mtbFile.ensureMetaDataIsInitialized()
+ addConsentData(mtbFile)
mtbFile.pseudonymizeWith(pseudonymizeService)
@@ -233,6 +249,25 @@ class ExtensionsTest {
assertThat(mtbFile.serialized()).doesNotContain(CLEAN_PATIENT_ID)
}
+ private fun addConsentData(mtbFile: Mtb) {
+ val gIcsConfigProperties = GIcsConfigProperties("", "", "")
+ val appConfigProperties = AppConfigProperties(null, emptyList())
+
+ val bundle = Bundle()
+ val dummyConsent = ConsentProcessorTest.getDummyGenomDeConsent()
+ dummyConsent.patient.reference = "Patient/$CLEAN_PATIENT_ID"
+ bundle.addEntry().resource = dummyConsent
+
+ ConsentProcessor(
+ appConfigProperties,
+ gIcsConfigProperties,
+ JacksonConfig().objectMapper(),
+ FhirContext.forR4(),
+ ConsentByMtbFile()
+ ).embedBroadConsentResources(mtbFile, bundle)
+
+ }
+
@Test
fun shouldNotThrowExceptionOnNullValues(@Mock pseudonymizeService: PseudonymizeService) {
doAnswer {
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt
new file mode 100644
index 0000000..38ce0b3
--- /dev/null
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt
@@ -0,0 +1,171 @@
+package dev.dnpm.etl.processor.services
+
+import ca.uhn.fhir.context.FhirContext
+import com.fasterxml.jackson.databind.ObjectMapper
+import dev.dnpm.etl.processor.config.AppConfigProperties
+import dev.dnpm.etl.processor.config.GIcsConfigProperties
+import dev.dnpm.etl.processor.config.JacksonConfig
+import dev.dnpm.etl.processor.consent.ConsentDomain
+import dev.dnpm.etl.processor.consent.GicsConsentService
+import dev.pcvolkmer.mv64e.mtb.*
+import org.assertj.core.api.Assertions.assertThat
+import org.hl7.fhir.r4.model.Bundle
+import org.hl7.fhir.r4.model.CodeableConcept
+import org.hl7.fhir.r4.model.Coding
+import org.hl7.fhir.r4.model.Consent
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.CsvSource
+import org.mockito.Mock
+import org.mockito.junit.jupiter.MockitoExtension
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+import org.springframework.core.io.ClassPathResource
+import java.io.IOException
+import java.io.InputStream
+import java.time.Instant
+import java.time.OffsetDateTime
+import java.util.*
+
+@ExtendWith(MockitoExtension::class)
+class ConsentProcessorTest {
+
+ private lateinit var appConfigProperties: AppConfigProperties
+ private lateinit var gicsConsentService: GicsConsentService
+ private lateinit var objectMapper: ObjectMapper
+ private lateinit var gIcsConfigProperties: GIcsConfigProperties
+ private lateinit var fhirContext: FhirContext
+ private lateinit var consentProcessor: ConsentProcessor
+
+ @BeforeEach
+ fun setups(
+ @Mock gicsConsentService: GicsConsentService,
+ ) {
+
+ this.gIcsConfigProperties = GIcsConfigProperties(null, null, null)
+ val jacksonConfig = JacksonConfig()
+ this.objectMapper = jacksonConfig.objectMapper()
+ this.fhirContext = JacksonConfig.fhirContext()
+ this.gicsConsentService = gicsConsentService
+ this.appConfigProperties = AppConfigProperties(null, emptyList())
+ this.consentProcessor =
+ ConsentProcessor(
+ appConfigProperties,
+ gIcsConfigProperties,
+ objectMapper,
+ fhirContext,
+ gicsConsentService
+ )
+ }
+
+ @Test
+ fun consentOk() {
+ assertThat(consentProcessor.toString()).isNotNull
+ // prep gICS response
+ doAnswer { getDummyBroadConsentBundle() }.whenever(gicsConsentService)
+ .getConsent(any(), any(), eq(ConsentDomain.BroadConsent))
+
+ doAnswer { Bundle() }.whenever(gicsConsentService)
+ .getConsent(any(), any(), eq(ConsentDomain.Modelvorhaben64e))
+
+ val inputMtb = Mtb.builder()
+ .patient(Patient.builder().id("d611d429-5003-11f0-a144-661e92ac9503").build()).build()
+ val checkResult = consentProcessor.consentGatedCheckAndTryEmbedding(inputMtb)
+
+ assertThat(checkResult).isTrue
+ assertThat(inputMtb.metadata.researchConsents).hasSize(13)
+ }
+
+ companion object {
+ fun getDummyGenomDeConsent(): Consent {
+ val consent = Consent()
+ consent.id = "consent 1 id"
+ consent.patient.reference = "Patient/1234-pat1"
+
+ consent.provision.setType(
+ Consent.ConsentProvisionType.fromCode(
+ "deny"
+ )
+ )
+ consent.provision.period.start =
+ Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
+ consent.provision.period.end =
+ Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
+
+ val addProvision1 = consent.provision.addProvision()
+ addProvision1.setType(Consent.ConsentProvisionType.fromCode("permit"))
+ addProvision1.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
+ addProvision1.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
+ addProvision1.code.addLast(
+ CodeableConcept(
+ Coding(
+ "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV",
+ "Teilnahme",
+ "Teilnahme am Modellvorhaben und Einwilligung zur Genomsequenzierung"
+ )
+ )
+ )
+
+ val addProvision2 = consent.provision.addProvision()
+ addProvision2.setType(Consent.ConsentProvisionType.fromCode("deny"))
+ addProvision2.period.start = Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))
+ addProvision2.period.end = Date.from(Instant.parse("3000-01-01T00:00:00.00Z"))
+ addProvision2.code.addLast(
+ CodeableConcept(
+ Coding(
+ "https://ths-greifswald.de/fhir/CodeSystem/gics/Policy/GenomDE_MV",
+ "Rekontaktierung",
+ "Re-Identifizierung meiner Daten über die Vertrauensstelle beim Robert Koch-Institut und in die erneute Kontaktaufnahme durch meine behandelnde Ärztin oder meinen behandelnden Arzt"
+ )
+ )
+ )
+ return consent
+ }
+ }
+
+ @ParameterizedTest
+ @CsvSource(
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-07-23T00:00:00+02:00,PERMIT,expect permit",
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-06-23T00:00:00+02:00,PERMIT,expect permit date is exactly on start",
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2055-06-23T00:00:00+02:00,PERMIT,expect permit date is exactly on end",
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2021-06-23T00:00:00+02:00,NULL,date is before start",
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2060-06-23T00:00:00+02:00,NULL,date is after end",
+ "2.16.840.1.113883.3.1937.777.24.5.3.8,XXXX,2025-07-23T00:00:00+02:00,NULL,system not found - therefore expect NULL",
+ "2.16.840.1.113883.3.1937.777.24.5.3.27,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-07-23T00:00:00+02:00,DENY,provision is denied"
+ )
+ fun getProvisionTypeByPolicyCode(
+ code: String?, system: String?, timeStamp: String, expected: String?,
+ desc: String?
+ ) {
+ val testData = getDummyBroadConsentBundle()
+
+ val requestDate = Date.from(OffsetDateTime.parse(timeStamp).toInstant())
+
+ val result: Consent.ConsentProvisionType =
+ consentProcessor.getProvisionTypeByPolicyCode(testData, code, system, requestDate)
+ assertThat(result).isNotNull()
+
+
+ assertThat(result).`as`(desc)
+ .isEqualTo(Consent.ConsentProvisionType.valueOf(expected!!))
+ }
+
+ fun getDummyBroadConsentBundle(): Bundle {
+ val bundle: InputStream?
+ try {
+ bundle = ClassPathResource(
+ "fake_broadConsent_gics_response_permit.json"
+ ).getInputStream()
+ } catch (e: IOException) {
+ throw RuntimeException(e)
+ }
+
+ return FhirContext.forR4().newJsonParser()
+ .parseResource<Bundle>(Bundle::class.java, bundle)
+ }
+
+} \ No newline at end of file
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt
index fe61852..b36c696 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt
@@ -25,6 +25,8 @@ import dev.dnpm.etl.processor.Fingerprint
import dev.dnpm.etl.processor.PatientId
import dev.dnpm.etl.processor.PatientPseudonym
import dev.dnpm.etl.processor.config.AppConfigProperties
+import dev.dnpm.etl.processor.consent.GicsConsentService
+import dev.dnpm.etl.processor.consent.TtpConsentStatus
import dev.dnpm.etl.processor.monitoring.Request
import dev.dnpm.etl.processor.monitoring.RequestStatus
import dev.dnpm.etl.processor.monitoring.RequestType
@@ -58,7 +60,7 @@ class RequestProcessorTest {
private lateinit var requestService: RequestService
private lateinit var applicationEventPublisher: ApplicationEventPublisher
private lateinit var appConfigProperties: AppConfigProperties
-
+ private lateinit var consentProcessor: ConsentProcessor
private lateinit var requestProcessor: RequestProcessor
@BeforeEach
@@ -67,7 +69,8 @@ class RequestProcessorTest {
@Mock transformationService: TransformationService,
@Mock sender: RestMtbFileSender,
@Mock requestService: RequestService,
- @Mock applicationEventPublisher: ApplicationEventPublisher
+ @Mock applicationEventPublisher: ApplicationEventPublisher,
+ @Mock consentProcessor: ConsentProcessor
) {
this.pseudonymizeService = pseudonymizeService
this.transformationService = transformationService
@@ -75,6 +78,7 @@ class RequestProcessorTest {
this.requestService = requestService
this.applicationEventPublisher = applicationEventPublisher
this.appConfigProperties = AppConfigProperties(null)
+ this.consentProcessor = consentProcessor
val objectMapper = ObjectMapper()
@@ -85,7 +89,8 @@ class RequestProcessorTest {
requestService,
objectMapper,
applicationEventPublisher,
- appConfigProperties
+ appConfigProperties,
+ consentProcessor
)
}
@@ -343,7 +348,10 @@ class RequestProcessorTest {
MtbFileSender.Response(status = RequestStatus.UNKNOWN)
}.whenever(sender).send(any<DeleteRequest>())
- this.requestProcessor.processDeletion(TEST_PATIENT_ID)
+ this.requestProcessor.processDeletion(
+ TEST_PATIENT_ID,
+ isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE
+ )
val requestCaptor = argumentCaptor<Request>()
verify(requestService, times(1)).save(requestCaptor.capture())
@@ -361,7 +369,10 @@ class RequestProcessorTest {
MtbFileSender.Response(status = RequestStatus.SUCCESS)
}.whenever(sender).send(any<DeleteRequest>())
- this.requestProcessor.processDeletion(TEST_PATIENT_ID)
+ this.requestProcessor.processDeletion(
+ TEST_PATIENT_ID,
+ isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE
+ )
val eventCaptor = argumentCaptor<ResponseEvent>()
verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
@@ -379,7 +390,10 @@ class RequestProcessorTest {
MtbFileSender.Response(status = RequestStatus.ERROR)
}.whenever(sender).send(any<DeleteRequest>())
- this.requestProcessor.processDeletion(TEST_PATIENT_ID)
+ this.requestProcessor.processDeletion(
+ TEST_PATIENT_ID,
+ isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE
+ )
val eventCaptor = argumentCaptor<ResponseEvent>()
verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture())
@@ -391,7 +405,10 @@ class RequestProcessorTest {
fun testShouldSendDeleteRequestWithPseudonymErrorAndSaveErrorRequestStatus() {
doThrow(RuntimeException()).whenever(pseudonymizeService).patientPseudonym(anyValueClass())
- this.requestProcessor.processDeletion(TEST_PATIENT_ID)
+ this.requestProcessor.processDeletion(
+ TEST_PATIENT_ID,
+ isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE
+ )
val requestCaptor = argumentCaptor<Request>()
verify(requestService, times(1)).save(requestCaptor.capture())
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt
index 487b502..113245a 100644
--- a/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt
+++ b/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt
@@ -19,14 +19,23 @@
package dev.dnpm.etl.processor.services
-import com.fasterxml.jackson.databind.ObjectMapper
import de.ukw.ccc.bwhc.dto.Consent
import de.ukw.ccc.bwhc.dto.Diagnosis
import de.ukw.ccc.bwhc.dto.Icd10
import de.ukw.ccc.bwhc.dto.MtbFile
+import dev.dnpm.etl.processor.config.JacksonConfig
+import dev.pcvolkmer.mv64e.mtb.ConsentProvision
+import dev.pcvolkmer.mv64e.mtb.ModelProjectConsent
+import dev.pcvolkmer.mv64e.mtb.ModelProjectConsentPurpose
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
+import dev.pcvolkmer.mv64e.mtb.Mtb
+import dev.pcvolkmer.mv64e.mtb.MvhMetadata
+import dev.pcvolkmer.mv64e.mtb.Provision
+import org.hl7.fhir.instance.model.api.IBaseResource
+import java.time.Instant
+import java.util.Date
class TransformationServiceTest {
@@ -35,7 +44,7 @@ class TransformationServiceTest {
@BeforeEach
fun setup() {
this.service = TransformationService(
- ObjectMapper(), listOf(
+ JacksonConfig().objectMapper(), listOf(
Transformation.of("consent.status") from Consent.Status.ACTIVE to Consent.Status.REJECTED,
Transformation.of("diagnoses[*].icd10.version") from "2013" to "2014",
)
@@ -92,4 +101,59 @@ class TransformationServiceTest {
assertThat(actual.consent.status).isEqualTo(Consent.Status.REJECTED)
}
+ @Test
+ fun shouldTransformConsentValues() {
+ val mtbFile = MtbFile.builder().withDiagnoses(
+ listOf(
+ Diagnosis.builder().withId("1234").withIcd10(Icd10("F79.9").also {
+ it.version = "2013"
+ }).build(),
+ Diagnosis.builder().withId("5678").withIcd10(Icd10("F79.8").also {
+ it.version = "2019"
+ }).build()
+ )
+ ).build()
+
+ val actual = this.service.transform(mtbFile)
+
+ assertThat(actual).isNotNull
+ assertThat(actual.diagnoses[0].icd10.code).isEqualTo("F79.9")
+ assertThat(actual.diagnoses[0].icd10.version).isEqualTo("2014")
+ assertThat(actual.diagnoses[1].icd10.code).isEqualTo("F79.8")
+ assertThat(actual.diagnoses[1].icd10.version).isEqualTo("2019")
+ }
+
+ @Test
+ fun shouldTransformConsent() {
+ val mvhMetadata = MvhMetadata.builder().transferTan("transfertan12345").build()
+
+ assertThat(mvhMetadata).isNotNull
+ mvhMetadata.modelProjectConsent =
+ ModelProjectConsent.builder().date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z")))
+ .version("1").provisions(
+ listOf(
+ Provision.builder().type(ConsentProvision.PERMIT)
+ .purpose(ModelProjectConsentPurpose.SEQUENCING)
+ .date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build(),
+ Provision.builder().type(ConsentProvision.PERMIT)
+ .purpose(ModelProjectConsentPurpose.REIDENTIFICATION)
+ .date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build(),
+ Provision.builder().type(ConsentProvision.DENY)
+ .purpose(ModelProjectConsentPurpose.CASE_IDENTIFICATION)
+ .date(Date.from(Instant.parse("2025-06-23T00:00:00.00Z"))).build()
+ )
+ ).build()
+ val consent = ConsentProcessorTest.getDummyGenomDeConsent()
+
+ mvhMetadata.researchConsents = mutableListOf()
+ mvhMetadata.researchConsents.add(mapOf(consent.id to consent as IBaseResource))
+
+ val mtbFile = Mtb.builder().metadata(mvhMetadata).build()
+
+ val transformed = service.transform(mtbFile)
+ assertThat(transformed.metadata.modelProjectConsent.date).isNotNull
+
+ }
+
+
} \ No newline at end of file