diff options
Diffstat (limited to 'src/test/kotlin/dev/dnpm/etl')
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 |
