diff options
Diffstat (limited to 'src/test')
14 files changed, 1088 insertions, 395 deletions
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/config/Jackson3ConfigTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/config/Jackson3ConfigTest.kt index b940537..d543f87 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/config/Jackson3ConfigTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/config/Jackson3ConfigTest.kt @@ -21,8 +21,9 @@ package dev.dnpm.etl.processor.config import com.fasterxml.jackson.databind.node.ObjectNode -import dev.pcvolkmer.mv64e.mtb.Mtb -import dev.pcvolkmer.mv64e.mtb.MvhMetadata +import dev.pcvolkmer.mv64e.model.MvhMetadata +import dev.pcvolkmer.mv64e.model.PatientRecord +import dev.pcvolkmer.mv64e.model.ResearchConsent import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -78,10 +79,10 @@ class Jackson3ConfigTest { val inputConsentJson = Objects.requireNonNull(this.javaClass.classLoader.getResourceAsStream("fake_broadConsent_mii_response_permit.json"))?.readAllBytes()?.decodeToString() - val mtb = this.jacksonConfig.jsonMapper().readValue<Mtb>(inputMtbFileJson, Mtb::class.java) + val mtb = this.jacksonConfig.jsonMapper().readValue<PatientRecord>(inputMtbFileJson, PatientRecord::class.java) // Still use Jackson2 ObjectMapper since MTB DTO requires Jackson2 ObjectNode val consentJsonNode = Jackson2Config().objectMapper().readTree(inputConsentJson) - mtb.metadata = MvhMetadata.builder().researchConsents(listOf(MvhMetadata.ResearchConsent.from(consentJsonNode as ObjectNode))).build() + mtb.metadata = MvhMetadata.builder().researchConsents(listOf(ResearchConsent.from(consentJsonNode as ObjectNode))).build() val actual = this.jacksonConfig.jsonMapper().writeValueAsString(mtb) diff --git a/src/test/kotlin/dev/dnpm/etl/processor/config/JacksonConfigTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/config/JacksonConfigTest.kt index e734c6f..4411e70 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/config/JacksonConfigTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/config/JacksonConfigTest.kt @@ -21,8 +21,9 @@ package dev.dnpm.etl.processor.config import com.fasterxml.jackson.databind.node.ObjectNode -import dev.pcvolkmer.mv64e.mtb.Mtb -import dev.pcvolkmer.mv64e.mtb.MvhMetadata +import dev.pcvolkmer.mv64e.model.MvhMetadata +import dev.pcvolkmer.mv64e.model.PatientRecord +import dev.pcvolkmer.mv64e.model.ResearchConsent import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -78,9 +79,9 @@ class JacksonConfigTest { val inputConsentJson = Objects.requireNonNull(this.javaClass.classLoader.getResourceAsStream("fake_broadConsent_mii_response_permit.json"))?.readAllBytes()?.decodeToString() - val mtb = this.jacksonConfig.objectMapper().readValue<Mtb>(inputMtbFileJson, Mtb::class.java) + val mtb = this.jacksonConfig.objectMapper().readValue<PatientRecord>(inputMtbFileJson, PatientRecord::class.java) val consentJsonNode = this.jacksonConfig.objectMapper().readTree(inputConsentJson) - mtb.metadata = MvhMetadata.builder().researchConsents(listOf(MvhMetadata.ResearchConsent.from(consentJsonNode as ObjectNode))).build() + mtb.metadata = MvhMetadata.builder().researchConsents(listOf(ResearchConsent.from(consentJsonNode as ObjectNode))).build() val actual = this.jacksonConfig.objectMapper().writeValueAsString(mtb) diff --git a/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt deleted file mode 100644 index 30e3823..0000000 --- a/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * This file is part of ETL-Processor - * - * Copyright (c) 2023 Comprehensive Cancer Center Mainfranken - * Copyright (c) 2026 Paul-Christian Volkmer, 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 <https://www.gnu.org/licenses/>. - */ - -package dev.dnpm.etl.processor.consent - -import ca.uhn.fhir.context.FhirContext -import dev.dnpm.etl.processor.config.AppConfigProperties -import dev.dnpm.etl.processor.config.GIcsConfigProperties -import dev.dnpm.etl.processor.services.ConsentProcessor -import org.assertj.core.api.Assertions.assertThat -import org.hl7.fhir.r4.model.Bundle -import org.hl7.fhir.r4.model.Consent -import org.junit.jupiter.api.BeforeEach -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 tools.jackson.databind.json.JsonMapper -import java.util.* - -@ExtendWith(MockitoExtension::class) -class ConsentProcessorTest { - - lateinit var consentProcessor: ConsentProcessor - - val jsonMapper = JsonMapper() - val fhirContext = FhirContext.forR4() - - @BeforeEach - fun setup(@Mock consentService: IConsentService) { - val appConfigProperties = AppConfigProperties() - val gIcsConfigProperties = GIcsConfigProperties("http://localhost") - - this.consentProcessor = - ConsentProcessor( - appConfigProperties, - gIcsConfigProperties, - jsonMapper, - fhirContext, - consentService, - ) - } - - @ParameterizedTest - @CsvSource(value = ["permittedConsentBundle.json,permit", "deniedConsentBundle.json,deny"]) - fun checkGetProvisionTypeByPolicyCode(filename: String, expected: String) { - val bundle = - fhirContext - .newJsonParser() - .parseResource(this.javaClass.classLoader.getResourceAsStream(filename)) - assertThat(bundle).isInstanceOf(Bundle::class.java) - - val actual = - consentProcessor.getProvisionTypeByPolicyCode( - bundle as Bundle, - Date(), - ConsentDomain.BROAD_CONSENT, - ) - - assertThat(actual).isEqualTo(Consent.ConsentProvisionType.valueOf(expected.uppercase())) - } -} diff --git a/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt index 284c552..3cf54fe 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt @@ -21,7 +21,16 @@ package dev.dnpm.etl.processor.consent import dev.dnpm.etl.processor.ArgProvider -import dev.pcvolkmer.mv64e.mtb.* +import dev.pcvolkmer.mv64e.model.ConsentProvisionType +import dev.pcvolkmer.mv64e.model.GenderCoding +import dev.pcvolkmer.mv64e.model.ModelProjectConsentPurpose +import dev.pcvolkmer.mv64e.model.MtbEpisodeOfCare +import dev.pcvolkmer.mv64e.model.MvhMetadata +import dev.pcvolkmer.mv64e.model.MvhMetadataModelProjectConsent +import dev.pcvolkmer.mv64e.model.MvhMetadataModelProjectConsentProvisionsInner +import dev.pcvolkmer.mv64e.model.Patient +import dev.pcvolkmer.mv64e.model.PatientRecord +import dev.pcvolkmer.mv64e.model.Reference import java.time.Instant import java.util.* import org.assertj.core.api.Assertions.assertThat @@ -54,7 +63,7 @@ class Dnpm21BasedConsentEvaluatorTest { @ParameterizedTest @ArgumentsSource(WithGicsMtbFileProvider::class) fun test( - mtbFile: Mtb, + mtbFile: PatientRecord, ttpConsentStatus: TtpConsentStatus, expectedConsentEvaluation: ConsentEvaluation, ) { @@ -77,7 +86,7 @@ class Dnpm21BasedConsentEvaluatorTest { @ParameterizedTest @ArgumentsSource(MtbFileProvider::class) - fun test(mtbFile: Mtb, expectedConsentEvaluation: ConsentEvaluation) { + fun test(mtbFile: PatientRecord, expectedConsentEvaluation: ConsentEvaluation) { assertThat(consentEvaluator.check(mtbFile)).isEqualTo(expectedConsentEvaluation) } } @@ -88,120 +97,120 @@ class Dnpm21BasedConsentEvaluatorTest { ArgProvider( // Has file ModelProjectConsent and broad consent => consent given Arguments.of( - buildMtb(ConsentProvision.PERMIT), + buildMtb(ConsentProvisionType.PERMIT), TtpConsentStatus.BROAD_CONSENT_GIVEN, ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true), ), // Has file ModelProjectConsent and broad consent missing => no consent given Arguments.of( - buildMtb(ConsentProvision.PERMIT), + buildMtb(ConsentProvisionType.PERMIT), TtpConsentStatus.BROAD_CONSENT_MISSING, ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING, false), ), // Has file ModelProjectConsent and broad consent missing or rejected => no consent given Arguments.of( - buildMtb(ConsentProvision.PERMIT), + buildMtb(ConsentProvisionType.PERMIT), TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, false), ), // Has file ModelProjectConsent and MV consent => consent given Arguments.of( - buildMtb(ConsentProvision.PERMIT), + buildMtb(ConsentProvisionType.PERMIT), TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT, ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT, true), ), // Has file ModelProjectConsent and MV consent rejected => no consent given Arguments.of( - buildMtb(ConsentProvision.PERMIT), + buildMtb(ConsentProvisionType.PERMIT), TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED, ConsentEvaluation(TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED, false), ), // Has file ModelProjectConsent and MV consent missing => no consent given Arguments.of( - buildMtb(ConsentProvision.PERMIT), + buildMtb(ConsentProvisionType.PERMIT), TtpConsentStatus.GENOM_DE_CONSENT_MISSING, ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_MISSING, false), ), // Has file ModelProjectConsent and no broad consent result => consent given Arguments.of( - buildMtb(ConsentProvision.PERMIT), + buildMtb(ConsentProvisionType.PERMIT), TtpConsentStatus.UNKNOWN_CHECK_FILE, ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, true), ), // Has file ModelProjectConsent and failed to ask => no consent given Arguments.of( - buildMtb(ConsentProvision.PERMIT), + buildMtb(ConsentProvisionType.PERMIT), TtpConsentStatus.FAILED_TO_ASK, ConsentEvaluation(TtpConsentStatus.FAILED_TO_ASK, false), ), // File ModelProjectConsent rejected and broad consent => consent given Arguments.of( - buildMtb(ConsentProvision.DENY), + buildMtb(ConsentProvisionType.DENY), TtpConsentStatus.BROAD_CONSENT_GIVEN, ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true), ), // File ModelProjectConsent rejected and broad consent missing => no consent given Arguments.of( - buildMtb(ConsentProvision.DENY), + buildMtb(ConsentProvisionType.DENY), TtpConsentStatus.BROAD_CONSENT_MISSING, ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING, false), ), // File ModelProjectConsent rejected and broad consent missing or rejected => no consent // given Arguments.of( - buildMtb(ConsentProvision.DENY), + buildMtb(ConsentProvisionType.DENY), TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, false), ), // File ModelProjectConsent rejected and MV consent => consent given Arguments.of( - buildMtb(ConsentProvision.DENY), + buildMtb(ConsentProvisionType.DENY), TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT, ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_SEQUENCING_PERMIT, true), ), // File ModelProjectConsent rejected and MV consent rejected => no consent given Arguments.of( - buildMtb(ConsentProvision.DENY), + buildMtb(ConsentProvisionType.DENY), TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED, ConsentEvaluation(TtpConsentStatus.GENOM_DE_SEQUENCING_REJECTED, false), ), // File ModelProjectConsent rejected and MV consent missing => no consent given Arguments.of( - buildMtb(ConsentProvision.DENY), + buildMtb(ConsentProvisionType.DENY), TtpConsentStatus.GENOM_DE_CONSENT_MISSING, ConsentEvaluation(TtpConsentStatus.GENOM_DE_CONSENT_MISSING, false), ), // File ModelProjectConsent rejected and no broad consent result => no consent given Arguments.of( - buildMtb(ConsentProvision.DENY), + buildMtb(ConsentProvisionType.DENY), TtpConsentStatus.UNKNOWN_CHECK_FILE, ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false), ), // File ModelProjectConsent rejected and failed to ask => no consent given Arguments.of( - buildMtb(ConsentProvision.DENY), + buildMtb(ConsentProvisionType.DENY), TtpConsentStatus.FAILED_TO_ASK, ConsentEvaluation(TtpConsentStatus.FAILED_TO_ASK, false), ), ) { companion object { - fun buildMtb(consentProvision: ConsentProvision): Mtb { - return Mtb.builder() + fun buildMtb(consentProvision: ConsentProvisionType): PatientRecord { + return PatientRecord.builder() .patient( Patient.builder() .id("TEST_12345678") .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))) - .gender(GenderCoding.builder().code(GenderCodingCode.MALE).build()) + .gender(GenderCoding.builder().code(GenderCoding.CodeEnum.MALE).build()) .build() ) .metadata( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf( - Provision.builder() + MvhMetadataModelProjectConsentProvisionsInner.builder() .date(Date()) .type(consentProvision) .purpose(ModelProjectConsentPurpose.SEQUENCING) @@ -229,55 +238,55 @@ class Dnpm21BasedConsentEvaluatorTest { ArgProvider( // Has file consent => consent given Arguments.of( - buildMtb(ConsentProvision.PERMIT), + buildMtb(ConsentProvisionType.PERMIT), ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, true), ), // File consent rejected => no consent given Arguments.of( - buildMtb(ConsentProvision.DENY), + buildMtb(ConsentProvisionType.DENY), ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false), ), // policy REIDENTIFICATION has no effect on ConsentEvaluation Arguments.of( - buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvision.DENY), + buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvisionType.DENY), ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false), ), Arguments.of( - buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvision.PERMIT), + buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvisionType.PERMIT), ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false), ), // policy CASE_IDENTIFICATION has no effect on ConsentEvaluation Arguments.of( - buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvision.DENY), + buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvisionType.DENY), ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false), ), Arguments.of( - buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvision.PERMIT), + buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvisionType.PERMIT), ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false), ), ) { companion object { - fun buildMtb(consentProvision: ConsentProvision): Mtb { + fun buildMtb(consentProvision: ConsentProvisionType): PatientRecord { return buildMtb(ModelProjectConsentPurpose.SEQUENCING, consentProvision) } - fun buildMtb(policy: ModelProjectConsentPurpose, consentProvision: ConsentProvision): Mtb { - return Mtb.builder() + fun buildMtb(policy: ModelProjectConsentPurpose, consentProvision: ConsentProvisionType): PatientRecord { + return PatientRecord.builder() .patient( Patient.builder() .id("TEST_12345678") .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))) - .gender(GenderCoding.builder().code(GenderCodingCode.MALE).build()) + .gender(GenderCoding.builder().code(GenderCoding.CodeEnum.MALE).build()) .build() ) .metadata( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf( - Provision.builder() + MvhMetadataModelProjectConsentProvisionsInner.builder() .date(Date()) .type(consentProvision) .purpose(policy) 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 eeb6288..9a5a2ee 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt @@ -25,7 +25,13 @@ import dev.dnpm.etl.processor.CustomMediaType import dev.dnpm.etl.processor.consent.ConsentEvaluator import dev.dnpm.etl.processor.consent.TtpConsentStatus import dev.dnpm.etl.processor.services.RequestProcessor -import dev.pcvolkmer.mv64e.mtb.* +import dev.pcvolkmer.mv64e.model.ConsentProvisionType +import dev.pcvolkmer.mv64e.model.ModelProjectConsentPurpose +import dev.pcvolkmer.mv64e.model.MvhMetadata +import dev.pcvolkmer.mv64e.model.MvhMetadataModelProjectConsent +import dev.pcvolkmer.mv64e.model.MvhMetadataModelProjectConsentProvisionsInner +import dev.pcvolkmer.mv64e.model.Patient +import dev.pcvolkmer.mv64e.model.PatientRecord import org.apache.kafka.clients.consumer.ConsumerRecord import org.apache.kafka.common.header.internals.RecordHeader import org.apache.kafka.common.header.internals.RecordHeaders @@ -69,16 +75,16 @@ class KafkaInputListenerTest { @Test fun shouldProcessMtbFileRequest() { val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("DUMMY_12345678").build()) .metadata( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf( - Provision.builder() - .type(ConsentProvision.PERMIT) + MvhMetadataModelProjectConsentProvisionsInner.builder() + .type(ConsentProvisionType.PERMIT) .purpose(ModelProjectConsentPurpose.SEQUENCING) .build() ) @@ -93,18 +99,18 @@ class KafkaInputListenerTest { ConsumerRecord("testtopic", 0, 0, "", this.jsonMapper.writeValueAsString(mtbFile)) ) - verify(requestProcessor, times(1)).processMtbFile(any<Mtb>()) + verify(requestProcessor, times(1)).processMtbFile(any<PatientRecord>()) } @Test fun shouldProcessRequestEvenIfNoConsentInformation() { val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("DUMMY_12345678").build()) .metadata( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf() ) @@ -117,22 +123,22 @@ class KafkaInputListenerTest { kafkaInputListener.onMessage( ConsumerRecord("testtopic", 0, 0, "", this.jsonMapper.writeValueAsString(mtbFile)) ) - verify(requestProcessor, times(1)).processMtbFile(any<Mtb>()) + verify(requestProcessor, times(1)).processMtbFile(any<PatientRecord>()) } @Test fun shouldProcessMtbFileRequestWithExistingRequestId() { val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("DUMMY_12345678").build()) .metadata( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf( - Provision.builder() - .type(ConsentProvision.PERMIT) + MvhMetadataModelProjectConsentProvisionsInner.builder() + .type(ConsentProvisionType.PERMIT) .purpose(ModelProjectConsentPurpose.SEQUENCING) .build() ) @@ -161,22 +167,22 @@ class KafkaInputListenerTest { ) ) - verify(requestProcessor, times(1)).processMtbFile(any<Mtb>(), anyValueClass()) + verify(requestProcessor, times(1)).processMtbFile(any<PatientRecord>(), anyValueClass()) } @Test fun shouldProcessRequestWithoutConsentGiven() { val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("DUMMY_12345678").build()) .metadata( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf( - Provision.builder() - .type(ConsentProvision.DENY) + MvhMetadataModelProjectConsentProvisionsInner.builder() + .type(ConsentProvisionType.DENY) .purpose(ModelProjectConsentPurpose.SEQUENCING) .build() ) @@ -204,22 +210,22 @@ class KafkaInputListenerTest { Optional.empty(), ) ) - verify(requestProcessor, times(1)).processMtbFile(any<Mtb>(), anyValueClass()) + verify(requestProcessor, times(1)).processMtbFile(any<PatientRecord>(), anyValueClass()) } @Test fun shouldProcessDnpmV2Request() { val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("DUMMY_12345678").build()) .metadata( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf( - Provision.builder() - .type(ConsentProvision.DENY) + MvhMetadataModelProjectConsentProvisionsInner.builder() + .type(ConsentProvisionType.DENY) .purpose(ModelProjectConsentPurpose.SEQUENCING) .build() ) @@ -255,22 +261,22 @@ class KafkaInputListenerTest { Optional.empty(), ) ) - verify(requestProcessor, times(1)).processMtbFile(any<Mtb>(), anyValueClass()) + verify(requestProcessor, times(1)).processMtbFile(any<PatientRecord>(), anyValueClass()) } @Test fun shouldProcessDnpmV2DeleteRequest() { val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("DUMMY_12345678").build()) .metadata( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf( - Provision.builder() - .type(ConsentProvision.DENY) + MvhMetadataModelProjectConsentProvisionsInner.builder() + .type(ConsentProvisionType.DENY) .purpose(ModelProjectConsentPurpose.SEQUENCING) .build() ) @@ -323,7 +329,7 @@ class KafkaInputListenerTest { ConsumerRecord("testtopic", 0, 0, "", content) ) - val result = verify(requestProcessor, times(1)).processMtbFile(any<Mtb>()) + val result = verify(requestProcessor, times(1)).processMtbFile(any<PatientRecord>()) assertThat(result).isFalse() } } 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 9ee4b2b..75d69dc 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt @@ -27,7 +27,17 @@ import dev.dnpm.etl.processor.consent.ConsentEvaluator import dev.dnpm.etl.processor.consent.TtpConsentStatus import dev.dnpm.etl.processor.input.Dnpm21MtbFile.Companion.buildMtb import dev.dnpm.etl.processor.services.RequestProcessor -import dev.pcvolkmer.mv64e.mtb.* +import dev.pcvolkmer.mv64e.model.ConsentProvisionType +import dev.pcvolkmer.mv64e.model.GenderCoding +import dev.pcvolkmer.mv64e.model.ModelProjectConsentPurpose +import dev.pcvolkmer.mv64e.model.MtbEpisodeOfCare +import dev.pcvolkmer.mv64e.model.MvhMetadata +import dev.pcvolkmer.mv64e.model.MvhMetadataModelProjectConsent +import dev.pcvolkmer.mv64e.model.MvhMetadataModelProjectConsentProvisionsInner +import dev.pcvolkmer.mv64e.model.Patient +import dev.pcvolkmer.mv64e.model.PatientRecord +import dev.pcvolkmer.mv64e.model.Reference +import org.flywaydb.core.internal.configuration.resolvers.ProvisionerMode import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @@ -78,7 +88,7 @@ class MtbFileRestControllerTest { .readAllBytes() .toString(Charsets.UTF_8) - whenever { requestProcessor.processMtbFile(any<Mtb>()) }.thenReturn(true) + whenever { requestProcessor.processMtbFile(any<PatientRecord>()) }.thenReturn(true) mockMvc .post("/mtb") { @@ -87,13 +97,13 @@ class MtbFileRestControllerTest { } .andExpect { status { isAccepted() } } - verify(requestProcessor, times(1)).processMtbFile(any<Mtb>()) + verify(requestProcessor, times(1)).processMtbFile(any<PatientRecord>()) } @ParameterizedTest @ArgumentsSource(Dnpm21MtbFile::class) - fun shouldProcessPostRequest(mtb: Mtb) { - whenever { requestProcessor.processMtbFile(any<Mtb>()) }.thenReturn(true) + fun shouldProcessPostRequest(mtb: PatientRecord) { + whenever { requestProcessor.processMtbFile(any<PatientRecord>()) }.thenReturn(true) mockMvc .post("/mtbfile") { @@ -132,12 +142,12 @@ class MtbFileRestControllerTest { buildMtb( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf( - Provision.builder() + MvhMetadataModelProjectConsentProvisionsInner.builder() .date(Date()) - .type(ConsentProvision.PERMIT) + .type(ConsentProvisionType.PERMIT) .purpose(ModelProjectConsentPurpose.SEQUENCING) .build() ) @@ -147,7 +157,7 @@ class MtbFileRestControllerTest { .build() ) - whenever { requestProcessor.processMtbFile(any<Mtb>()) }.thenReturn(true) + whenever { requestProcessor.processMtbFile(any<PatientRecord>()) }.thenReturn(true) mockMvc .post(url) { @@ -200,7 +210,7 @@ class Dnpm21MtbFile : Arguments.of( buildMtb( MvhMetadata.builder() - .modelProjectConsent(ModelProjectConsent.builder().build()) + .modelProjectConsent(MvhMetadataModelProjectConsent.builder().build()) .build() ) ), @@ -209,12 +219,12 @@ class Dnpm21MtbFile : buildMtb( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf( - Provision.builder() + MvhMetadataModelProjectConsentProvisionsInner.builder() .date(Date()) - .type(ConsentProvision.PERMIT) + .type(ConsentProvisionType.PERMIT) .purpose(ModelProjectConsentPurpose.SEQUENCING) .build() ) @@ -229,12 +239,12 @@ class Dnpm21MtbFile : buildMtb( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf( - Provision.builder() + MvhMetadataModelProjectConsentProvisionsInner.builder() .date(Date()) - .type(ConsentProvision.PERMIT) + .type(ConsentProvisionType.PERMIT) .purpose(ModelProjectConsentPurpose.SEQUENCING) .build() ) @@ -247,13 +257,13 @@ class Dnpm21MtbFile : ) { companion object { - fun buildMtb(metadata: MvhMetadata?): Mtb { - return Mtb.builder() + fun buildMtb(metadata: MvhMetadata?): PatientRecord { + return PatientRecord.builder() .patient( Patient.builder() .id("TEST_12345678") .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))) - .gender(GenderCoding.builder().code(GenderCodingCode.MALE).build()) + .gender(GenderCoding.builder().code(GenderCoding.CodeEnum.MALE).build()) .build() ) .metadata(metadata) diff --git a/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt index ee840a8..9384aae 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt @@ -25,7 +25,11 @@ import dev.dnpm.etl.processor.PatientPseudonym import dev.dnpm.etl.processor.RequestId import dev.dnpm.etl.processor.config.KafkaProperties import dev.dnpm.etl.processor.monitoring.RequestStatus -import dev.pcvolkmer.mv64e.mtb.* +import dev.pcvolkmer.mv64e.model.GenderCoding +import dev.pcvolkmer.mv64e.model.MtbEpisodeOfCare +import dev.pcvolkmer.mv64e.model.PatientRecord +import dev.pcvolkmer.mv64e.model.PeriodDate +import dev.pcvolkmer.mv64e.model.Reference import org.apache.kafka.clients.producer.ProducerRecord import org.apache.kafka.clients.producer.RecordMetadata import org.apache.kafka.common.TopicPartition @@ -252,13 +256,13 @@ class KafkaMtbFileSenderTest { val TEST_REQUEST_ID = RequestId("TestId") val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID") - fun dnpmV2MtbFile(): Mtb { - return Mtb().apply { + fun dnpmV2MtbFile(): PatientRecord { + return PatientRecord().apply { this.patient = - dev.pcvolkmer.mv64e.mtb.Patient().apply { + dev.pcvolkmer.mv64e.model.Patient().apply { this.id = "PID" this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + this.gender = GenderCoding().apply { this.code = GenderCoding.CodeEnum.MALE } } this.episodesOfCare = listOf( @@ -271,7 +275,7 @@ class KafkaMtbFileSenderTest { } } - fun dnmpV2kafkaRecordData(requestId: RequestId): Mtb { + fun dnmpV2kafkaRecordData(requestId: RequestId): PatientRecord { return DnpmV2MtbFileRequest(requestId, dnpmV2MtbFile()).content } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt index d95ced0..3a0b3f0 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt @@ -28,7 +28,12 @@ import dev.dnpm.etl.processor.config.AppConfiguration import dev.dnpm.etl.processor.config.RestTargetProperties import dev.dnpm.etl.processor.monitoring.ReportService import dev.dnpm.etl.processor.monitoring.RequestStatus -import dev.pcvolkmer.mv64e.mtb.* +import dev.pcvolkmer.mv64e.model.GenderCoding +import dev.pcvolkmer.mv64e.model.MtbEpisodeOfCare +import dev.pcvolkmer.mv64e.model.Patient +import dev.pcvolkmer.mv64e.model.PatientRecord +import dev.pcvolkmer.mv64e.model.PeriodDate +import dev.pcvolkmer.mv64e.model.Reference import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested @@ -194,13 +199,13 @@ class RestDipMtbFileSenderTest { val TEST_REQUEST_ID = RequestId("TestId") val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID") - fun dnpmV2MtbFile(): Mtb { - return Mtb().apply { + fun dnpmV2MtbFile(): PatientRecord { + return PatientRecord().apply { this.patient = Patient().apply { this.id = "PID" this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + this.gender = GenderCoding().apply { this.code = GenderCoding.CodeEnum.MALE } } this.episodesOfCare = listOf( 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 abc17c6..93c9106 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt @@ -27,7 +27,17 @@ import dev.dnpm.etl.processor.config.JacksonConfig import dev.dnpm.etl.processor.consent.MtbFileConsentService import dev.dnpm.etl.processor.services.ConsentProcessor import dev.dnpm.etl.processor.services.ConsentProcessorTest -import dev.pcvolkmer.mv64e.mtb.* +import dev.pcvolkmer.mv64e.model.GenderCoding +import dev.pcvolkmer.mv64e.model.Msi +import dev.pcvolkmer.mv64e.model.MtbDiagnosis +import dev.pcvolkmer.mv64e.model.MtbEpisodeOfCare +import dev.pcvolkmer.mv64e.model.MtbSystemicTherapy +import dev.pcvolkmer.mv64e.model.OncoProcedure +import dev.pcvolkmer.mv64e.model.Patient +import dev.pcvolkmer.mv64e.model.PatientRecord +import dev.pcvolkmer.mv64e.model.PeriodDate +import dev.pcvolkmer.mv64e.model.Reference +import dev.pcvolkmer.mv64e.model.TumorSpecimen import org.assertj.core.api.Assertions.assertThat import org.hl7.fhir.r4.model.Bundle import org.junit.jupiter.api.Nested @@ -56,12 +66,12 @@ class ExtensionsTest { val FAKE_MTB_FILE_PATH = "mv64e-mtb-fake-patient.json" val CLEAN_PATIENT_ID = "644bae7a-56f6-4ee8-b02f-c532e65af5b1" - private fun fakeMtbFile(): Mtb { + private fun fakeMtbFile(): PatientRecord { val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream - return getJsonMapper().readValue(mtbFile, Mtb::class.java) + return getJsonMapper().readValue(mtbFile, PatientRecord::class.java) } - private fun Mtb.serialized(): String { + private fun PatientRecord.serialized(): String { return getJsonMapper().writeValueAsString(this) } @@ -80,11 +90,11 @@ class ExtensionsTest { mtbFile.pseudonymizeWith(pseudonymizeService) - assertThat(mtbFile.patient.id).isEqualTo("PSEUDO-ID") + assertThat(mtbFile.patient?.id).isEqualTo("PSEUDO-ID") assertThat(mtbFile.serialized()).doesNotContain(CLEAN_PATIENT_ID) } - private fun addConsentData(mtbFile: Mtb) { + private fun addConsentData(mtbFile: PatientRecord) { val gIcsConfigProperties = GIcsConfigProperties("", "", "") val appConfigProperties = AppConfigProperties(emptyList()) @@ -115,12 +125,12 @@ class ExtensionsTest { doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() val mtbFile = - Mtb().apply { + PatientRecord().apply { this.patient = Patient().apply { this.id = "PID" this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + this.gender = GenderCoding().apply { this.code = GenderCoding.CodeEnum.MALE } } this.episodesOfCare = listOf( @@ -136,7 +146,7 @@ class ExtensionsTest { mtbFile.anonymizeContentWith(pseudonymizeService) assertThat(mtbFile.episodesOfCare).hasSize(1) - assertThat(mtbFile.episodesOfCare.map { it.id }).isNotNull + assertThat(mtbFile.episodesOfCare?.map { it.id }).isNotNull } @Test @@ -153,20 +163,20 @@ class ExtensionsTest { val mtbFile = fakeMtbFile() /** replace hex values with random long, so our test does not match false positives */ - mtbFile.ngsReports.forEach { report -> - report.results.simpleVariants.forEach { simpleVariant -> - simpleVariant.externalIds.forEach { extIdValue -> + mtbFile.ngsReports?.forEach { report -> + report.results.simpleVariants?.forEach { simpleVariant -> + simpleVariant.externalIds?.forEach { extIdValue -> extIdValue.value = Math.random().toLong().toString() } } } - mtbFile.ngsReports.forEach { report -> - report.results.rnaFusions.forEach { simpleVariant -> - simpleVariant.externalIds.forEach { extIdValue -> + mtbFile.ngsReports?.forEach { report -> + report.results.rnaFusions?.forEach { simpleVariant -> + simpleVariant.externalIds?.forEach { extIdValue -> extIdValue.value = Math.random().toLong().toString() } - simpleVariant.fusionPartner3Prime?.transcriptId?.value = Math.random().toLong().toString() - simpleVariant.fusionPartner5Prime?.transcriptId?.value = Math.random().toLong().toString() + simpleVariant.fusionPartner3prime?.transcriptId?.value = Math.random().toLong().toString() + simpleVariant.fusionPartner5prime?.transcriptId?.value = Math.random().toLong().toString() simpleVariant.externalIds?.forEach { it?.value = Math.random().toLong().toString() } } } @@ -203,12 +213,12 @@ class ExtensionsTest { doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() val mtbFile = - Mtb().apply { + PatientRecord().apply { this.patient = Patient().apply { this.id = "PID" this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + this.gender = GenderCoding().apply { this.code = GenderCoding.CodeEnum.MALE } } this.diagnoses = listOf(MtbDiagnosis().apply { this.id = "Diagnosis-1" }) this.episodesOfCare = @@ -244,12 +254,12 @@ class ExtensionsTest { mtbFile.pseudonymizeWith(pseudonymizeService) mtbFile.anonymizeContentWith(pseudonymizeService) - assertThat(mtbFile.diagnoses.first().id) - .isEqualTo(mtbFile.episodesOfCare.first().diagnoses.first().id) - assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.guidelineTherapies.first().reason.id) - assertThat(mtbFile.diagnoses.first().id) - .isEqualTo(mtbFile.guidelineProcedures.first().reason.id) - assertThat(mtbFile.diagnoses.first().id).isEqualTo(mtbFile.specimens.first().diagnosis.id) + assertThat(mtbFile.diagnoses?.first()?.id) + .isEqualTo(mtbFile.episodesOfCare?.first()?.diagnoses?.first()?.id) + assertThat(mtbFile.diagnoses?.first()?.id).isEqualTo(mtbFile.guidelineTherapies?.first()?.reason?.id) + assertThat(mtbFile.diagnoses?.first()?.id) + .isEqualTo(mtbFile.guidelineProcedures?.first()?.reason?.id) + assertThat(mtbFile.diagnoses?.first()?.id).isEqualTo(mtbFile.specimens?.first()?.diagnosis?.id) } @Test @@ -265,12 +275,12 @@ class ExtensionsTest { doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() val mtbFile = - Mtb().apply { + PatientRecord().apply { this.patient = Patient().apply { this.id = "PID" this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + this.gender = GenderCoding().apply { this.code = GenderCoding.CodeEnum.MALE } } this.msiFindings = listOf( @@ -286,10 +296,10 @@ class ExtensionsTest { mtbFile.anonymizeContentWith(pseudonymizeService) assertThat(mtbFile.msiFindings).isNotNull - assertThat(mtbFile.msiFindings[1]) + assertThat(mtbFile.msiFindings?.get(1)) .satisfiesAnyOf( - { assertThat(it.id).isNull() }, - { assertThat(it.id).isEqualTo("TESTDOMAIN44e20a53bbbf9f3ae39626d05df7014dcd77d6098") }, + { assertThat(it?.id).isNull() }, + { assertThat(it?.id).isEqualTo("TESTDOMAIN44e20a53bbbf9f3ae39626d05df7014dcd77d6098") }, ) } } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt index 7c6d6c9..00487d0 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt @@ -21,7 +21,11 @@ package dev.dnpm.etl.processor.pseudonym import dev.dnpm.etl.processor.config.PseudonymizeConfigProperties -import dev.pcvolkmer.mv64e.mtb.* +import dev.pcvolkmer.mv64e.model.MtbEpisodeOfCare +import dev.pcvolkmer.mv64e.model.Patient +import dev.pcvolkmer.mv64e.model.PatientRecord +import dev.pcvolkmer.mv64e.model.PeriodDate +import dev.pcvolkmer.mv64e.model.Reference import java.time.Instant import java.util.* import org.assertj.core.api.Assertions.assertThat @@ -37,7 +41,7 @@ import org.mockito.kotlin.whenever class PseudonymizeServiceTest { private val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .episodesOfCare( listOf( @@ -62,7 +66,7 @@ class PseudonymizeServiceTest { mtbFile.pseudonymizeWith(pseudonymizeService) - assertThat(mtbFile.patient.id).isEqualTo("123") + assertThat(mtbFile.patient?.id).isEqualTo("123") } @Test @@ -80,7 +84,7 @@ class PseudonymizeServiceTest { mtbFile.pseudonymizeWith(pseudonymizeService) - assertThat(mtbFile.patient.id).isEqualTo("UNKNOWN_123") + assertThat(mtbFile.patient?.id).isEqualTo("UNKNOWN_123") } @Test diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt index a85d555..6cb8c43 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt @@ -27,9 +27,11 @@ import dev.dnpm.etl.processor.config.JacksonConfig import dev.dnpm.etl.processor.consent.ConsentDomain import dev.dnpm.etl.processor.consent.GicsConsentService import dev.dnpm.etl.processor.consent.MtbFileConsentService -import dev.pcvolkmer.mv64e.mtb.Mtb -import dev.pcvolkmer.mv64e.mtb.MvhSubmissionType -import dev.pcvolkmer.mv64e.mtb.Patient +import dev.pcvolkmer.mv64e.model.BroadConsentReasonMissing +import dev.pcvolkmer.mv64e.model.MvhMetadata +import dev.pcvolkmer.mv64e.model.MvhSubmissionType +import dev.pcvolkmer.mv64e.model.Patient +import dev.pcvolkmer.mv64e.model.PatientRecord import org.assertj.core.api.Assertions.assertThat import org.hl7.fhir.r4.model.Bundle import org.hl7.fhir.r4.model.CodeableConcept @@ -46,6 +48,8 @@ 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.times +import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import org.springframework.core.io.ClassPathResource import tools.jackson.databind.json.JsonMapper @@ -99,13 +103,13 @@ class ConsentProcessorTest { .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E)) val inputMtb = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("d611d429-5003-11f0-a144-661e92ac9503").build()) .build() val checkResult = consentProcessor.consentGatedCheckAndTryEmbedding(inputMtb) assertThat(checkResult).isTrue - assertThat(inputMtb.metadata.researchConsents).isNotEmpty + assertThat(inputMtb.metadata?.researchConsents).isNotEmpty } @Test @@ -122,7 +126,7 @@ class ConsentProcessorTest { assertThat(consentProcessor.toString()).isNotNull val inputMtb = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("d611d429-5003-11f0-a144-661e92ac9503").build()) .build() val checkResult = consentProcessor.consentGatedCheckAndTryEmbedding(inputMtb) @@ -256,15 +260,56 @@ class ConsentProcessorTest { .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E)) val inputMtb = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("d611d429-5003-11f0-a144-661e92ac9503").build()) .build() val checkResult = fixture.consentGatedCheckAndTryEmbedding(inputMtb) assertThat(checkResult).isNotNull - if (isTestSubmission) assertThat(inputMtb.metadata.type).isEqualTo(MvhSubmissionType.TEST) + if (isTestSubmission) assertThat(inputMtb.metadata?.type).isEqualTo(MvhSubmissionType.TEST) else { - assertThat(inputMtb.metadata.type).isEqualTo(MvhSubmissionType.INITIAL) + assertThat(inputMtb.metadata?.type).isEqualTo(MvhSubmissionType.INITIAL) } } + + @Test + fun doNotRequestBroadConsentIfReasonMissingIsGiven() { + doAnswer { Bundle() } + .whenever(gicsConsentService) + .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E)) + + val inputMtb = + PatientRecord.builder() + .patient(Patient.builder().id("d611d429-5003-11f0-a144-661e92ac9503").build()) + .metadata(MvhMetadata.builder().reasonResearchConsentMissing(BroadConsentReasonMissing.OTHER_PATIENT_REASON).build()) + .build() + val checkResult = consentProcessor.consentGatedCheckAndTryEmbedding(inputMtb) + + verify(gicsConsentService, times(1)) + .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E)) + verify(gicsConsentService, times(0)) + .getConsent(any(), any(), eq(ConsentDomain.BROAD_CONSENT)) + + assertThat(checkResult).isFalse + assertThat(inputMtb.metadata?.researchConsents).isEmpty() + } + + @ParameterizedTest + @CsvSource(value = ["permittedConsentBundle.json,permit", "deniedConsentBundle.json,deny"]) + fun checkGetProvisionTypeByPolicyCode(filename: String, expected: String) { + val bundle = + fhirContext + .newJsonParser() + .parseResource(this.javaClass.classLoader.getResourceAsStream(filename)) + assertThat(bundle).isInstanceOf(Bundle::class.java) + + val actual = + consentProcessor.getProvisionTypeByPolicyCode( + bundle as Bundle, + Date(), + ConsentDomain.BROAD_CONSENT, + ) + + assertThat(actual).isEqualTo(Consent.ConsentProvisionType.valueOf(expected.uppercase())) + } } 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 6827a4f..17570d4 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt @@ -20,7 +20,6 @@ package dev.dnpm.etl.processor.services -import com.fasterxml.jackson.databind.ObjectMapper import dev.dnpm.etl.processor.* import dev.dnpm.etl.processor.config.AppConfigProperties import dev.dnpm.etl.processor.consent.TtpConsentStatus @@ -33,9 +32,19 @@ import dev.dnpm.etl.processor.output.DnpmV2MtbFileRequest import dev.dnpm.etl.processor.output.MtbFileSender import dev.dnpm.etl.processor.output.RestMtbFileSender import dev.dnpm.etl.processor.pseudonym.PseudonymizeService -import dev.pcvolkmer.mv64e.mtb.* +import dev.pcvolkmer.mv64e.model.ConsentProvisionType +import dev.pcvolkmer.mv64e.model.FollowUp +import dev.pcvolkmer.mv64e.model.ModelProjectConsentPurpose +import dev.pcvolkmer.mv64e.model.MtbEpisodeOfCare +import dev.pcvolkmer.mv64e.model.MvhMetadata +import dev.pcvolkmer.mv64e.model.MvhMetadataModelProjectConsent +import dev.pcvolkmer.mv64e.model.MvhMetadataModelProjectConsentProvisionsInner +import dev.pcvolkmer.mv64e.model.MvhSubmissionType +import dev.pcvolkmer.mv64e.model.Patient +import dev.pcvolkmer.mv64e.model.PatientRecord +import dev.pcvolkmer.mv64e.model.PeriodDate +import dev.pcvolkmer.mv64e.model.Reference import org.assertj.core.api.Assertions.assertThat -import org.hl7.fhir.r4.model.Consent import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @@ -123,12 +132,12 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .patientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .episodesOfCare( listOf( @@ -180,12 +189,12 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .patientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .episodesOfCare( listOf( @@ -241,12 +250,12 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .patientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .episodesOfCare( listOf( @@ -306,21 +315,21 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .genomDeTan(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .metadata( MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .provisions( listOf( - Provision.builder() - .type(ConsentProvision.PERMIT) + MvhMetadataModelProjectConsentProvisionsInner.builder() + .type(ConsentProvisionType.PERMIT) .purpose(ModelProjectConsentPurpose.SEQUENCING) .build() ) @@ -406,7 +415,7 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .genomDeTan(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) @@ -423,7 +432,7 @@ class RequestProcessorTest { ) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .metadata(MvhMetadata()) .episodesOfCare( @@ -446,8 +455,8 @@ class RequestProcessorTest { val requestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() verify(sender, times(1)).send(requestCaptor.capture()) assertThat(requestCaptor.firstValue).isNotNull - assertThat(requestCaptor.firstValue.content.metadata.type).isEqualTo(MvhSubmissionType.ADDITION) - assertThat(requestCaptor.firstValue.content.metadata.transferTan).isEqualTo("f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2") + assertThat(requestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.ADDITION) + assertThat(requestCaptor.firstValue.content.metadata?.transferTAN).isEqualTo("f2ca1bb6c7e907d06dafe4687e579fce76b37e4e93b7605022da52e6ccc26fd2") val eventCaptor = argumentCaptor<ResponseEvent>() verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) @@ -503,10 +512,10 @@ class RequestProcessorTest { doAnswer { it.arguments.first() } .whenever(transformationService) - .transform(any<Mtb>()) + .transform(any<PatientRecord>()) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .metadata(MvhMetadata()) .episodesOfCare( @@ -577,7 +586,7 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .patientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } .whenever(sender) @@ -586,7 +595,7 @@ class RequestProcessorTest { whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .episodesOfCare( listOf( @@ -630,7 +639,7 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .genomDeTan(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) @@ -647,7 +656,7 @@ class RequestProcessorTest { ) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .metadata(MvhMetadata()) .episodesOfCare( @@ -772,7 +781,7 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .patientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) @@ -789,7 +798,7 @@ class RequestProcessorTest { ) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .episodesOfCare( listOf( @@ -812,6 +821,7 @@ class RequestProcessorTest { verify(requestService, times(1)).save(requestCaptor.capture()) assertThat(requestCaptor.firstValue).isNotNull assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.BLOCKED_INITIAL) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(0) verify(applicationEventPublisher, times(0)).publishEvent(any()) verify(sender, times(0)).send(any<DnpmV2MtbFileRequest>()) @@ -872,7 +882,7 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .patientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } .whenever(sender) @@ -893,7 +903,7 @@ class RequestProcessorTest { ) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) .episodesOfCare( @@ -918,6 +928,7 @@ class RequestProcessorTest { assertThat(requestCaptor.firstValue).isNotNull assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.ADDITION) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(0) val eventCaptor = argumentCaptor<ResponseEvent>() verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) @@ -925,7 +936,7 @@ class RequestProcessorTest { val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() verify(sender, times(1)).send(sendRequestCaptor.capture()) - assertThat(sendRequestCaptor.firstValue.content.metadata.type).isEqualTo(MvhSubmissionType.ADDITION) + assertThat(sendRequestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.ADDITION) } @Test @@ -983,7 +994,7 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .patientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } .whenever(sender) @@ -1004,7 +1015,7 @@ class RequestProcessorTest { ) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) .episodesOfCare( @@ -1029,6 +1040,7 @@ class RequestProcessorTest { assertThat(requestCaptor.firstValue).isNotNull assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.ADDITION) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(0) val eventCaptor = argumentCaptor<ResponseEvent>() verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) @@ -1036,7 +1048,7 @@ class RequestProcessorTest { val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() verify(sender, times(1)).send(sendRequestCaptor.capture()) - assertThat(sendRequestCaptor.firstValue.content.metadata.type).isEqualTo(MvhSubmissionType.ADDITION) + assertThat(sendRequestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.ADDITION) } @Test @@ -1081,7 +1093,7 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .patientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } .whenever(sender) @@ -1102,7 +1114,7 @@ class RequestProcessorTest { ) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) .episodesOfCare( @@ -1127,6 +1139,7 @@ class RequestProcessorTest { assertThat(requestCaptor.firstValue).isNotNull assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.INITIAL) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(0) val eventCaptor = argumentCaptor<ResponseEvent>() verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) @@ -1134,13 +1147,58 @@ class RequestProcessorTest { val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() verify(sender, times(1)).send(sendRequestCaptor.capture()) - assertThat(sendRequestCaptor.firstValue.content.metadata.type).isEqualTo(MvhSubmissionType.INITIAL) + assertThat(sendRequestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.INITIAL) + } + + } + + @Nested + inner class FollowUpSubmission { + + private lateinit var pseudonymizeService: PseudonymizeService + private lateinit var transformationService: TransformationService + private lateinit var sender: MtbFileSender + 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 + private lateinit var jsonMapper: JsonMapper + + @BeforeEach + fun setup( + @Mock pseudonymizeService: PseudonymizeService, + @Mock transformationService: TransformationService, + @Mock sender: RestMtbFileSender, + @Mock requestService: RequestService, + @Mock applicationEventPublisher: ApplicationEventPublisher, + @Mock consentProcessor: ConsentProcessor, + ) { + this.pseudonymizeService = pseudonymizeService + this.transformationService = transformationService + this.sender = sender + this.requestService = requestService + this.applicationEventPublisher = applicationEventPublisher + this.appConfigProperties = AppConfigProperties() + this.consentProcessor = consentProcessor + this.jsonMapper = JsonMapper() + + requestProcessor = + RequestProcessor( + pseudonymizeService, + transformationService, + sender, + requestService, + jsonMapper, + applicationEventPublisher, + appConfigProperties, + consentProcessor, + ) } @Test fun testShouldSendFollowUpMtbFileIfUnacceptedErrorsAndAcceptedInitialFileWasSent() { - // One failed attempt and one successful but not accepted val lastRequests = listOf( Request( @@ -1192,7 +1250,7 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .patientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } .whenever(sender) @@ -1213,7 +1271,7 @@ class RequestProcessorTest { ) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) .episodesOfCare( @@ -1247,6 +1305,8 @@ class RequestProcessorTest { assertThat(requestCaptor.firstValue).isNotNull assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.FOLLOWUP) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(0) + assertThat(requestCaptor.firstValue.expectedFollowupCount).isEqualTo(1) val eventCaptor = argumentCaptor<ResponseEvent>() verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) @@ -1254,14 +1314,13 @@ class RequestProcessorTest { val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() verify(sender, times(1)).send(sendRequestCaptor.capture()) - assertThat(sendRequestCaptor.firstValue.content.metadata.type).isEqualTo(MvhSubmissionType.FOLLOWUP) + assertThat(sendRequestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.FOLLOWUP) } @Test fun testShouldSendFollowUpMtbFileAfterAdditionWasSent() { - // One failed attempt and one successful but not accepted val lastRequests = listOf( Request( @@ -1313,7 +1372,7 @@ class RequestProcessorTest { .whenever(pseudonymizeService) .patientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } .whenever(sender) @@ -1334,7 +1393,7 @@ class RequestProcessorTest { ) val mtbFile = - Mtb.builder() + PatientRecord.builder() .patient(Patient.builder().id("123").build()) .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) .episodesOfCare( @@ -1368,6 +1427,8 @@ class RequestProcessorTest { assertThat(requestCaptor.firstValue).isNotNull assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.FOLLOWUP) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(0) + assertThat(requestCaptor.firstValue.expectedFollowupCount).isEqualTo(1) val eventCaptor = argumentCaptor<ResponseEvent>() verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) @@ -1375,133 +1436,709 @@ class RequestProcessorTest { val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() verify(sender, times(1)).send(sendRequestCaptor.capture()) - assertThat(sendRequestCaptor.firstValue.content.metadata.type).isEqualTo(MvhSubmissionType.FOLLOWUP) + assertThat(sendRequestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.FOLLOWUP) } - } - @Test - fun testShouldSendAdditionMtbFileAfterFollowUpWasSent() { + @Test + fun testShouldSendFollowUpMtbFileAfterInitialWasSent() { - // One failed attempt and one successful but not accepted - val lastRequests = - listOf( - Request( - 1L, - randomRequestId(), - PatientPseudonym("TEST_12345678901"), - PatientId("P1"), - Fingerprint("initial"), - RequestType.MTB_FILE, - SubmissionType.INITIAL, - RequestStatus.ERROR, - Tan.empty(), - Instant.parse("2026-02-10T09:00:00Z"), - submissionAccepted = false, - ), - Request( - 2L, - randomRequestId(), - PatientPseudonym("TEST_12345678901"), - PatientId("P1"), - Fingerprint("initial"), - RequestType.MTB_FILE, - SubmissionType.INITIAL, - RequestStatus.WARNING, - Tan.empty(), - Instant.parse("2026-02-11T09:00:00Z"), - submissionAccepted = true, - ), - Request( - 3L, - randomRequestId(), - PatientPseudonym("TEST_12345678901"), - PatientId("P1"), - Fingerprint("initial"), - RequestType.MTB_FILE, - SubmissionType.FOLLOWUP, - RequestStatus.WARNING, - Tan.empty(), - Instant.parse("2026-05-20T09:00:00Z"), - submissionAccepted = false, + val lastRequests = + listOf( + Request( + 1L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.INITIAL, + RequestStatus.ERROR, + Tan.empty(), + Instant.parse("2026-02-10T09:00:00Z"), + submissionAccepted = false, + ), + Request( + 2L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.INITIAL, + RequestStatus.WARNING, + Tan.empty(), + Instant.parse("2026-05-20T09:00:00Z"), + submissionAccepted = true, + ) ) - ) - doAnswer { lastRequests } - .whenever(requestService) - .allRequestsByPatientPseudonym(anyValueClass()) + doAnswer { lastRequests } + .whenever(requestService) + .allRequestsByPatientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] as String } - .whenever(pseudonymizeService) - .patientPseudonym(anyValueClass()) + doAnswer { it.arguments[0] as String } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) - doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) - doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } - .whenever(sender) - .send(any<DnpmV2MtbFileRequest>()) + doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } + .whenever(sender) + .send(any<DnpmV2MtbFileRequest>()) - whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) + whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) - requestProcessor = - RequestProcessor( - pseudonymizeService, - transformationService, - sender, - requestService, - jsonMapper, - applicationEventPublisher, - AppConfigProperties(postInitialSubmissionBlock = true), - consentProcessor, - ) + requestProcessor = + RequestProcessor( + pseudonymizeService, + transformationService, + sender, + requestService, + jsonMapper, + applicationEventPublisher, + AppConfigProperties(postInitialSubmissionBlock = true), + consentProcessor, + ) - val mtbFile = - Mtb.builder() - .patient(Patient.builder().id("123").build()) - .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) - .episodesOfCare( - listOf( - MtbEpisodeOfCare.builder() - .id("1") - .patient(Reference.builder().id("123").build()) - .period( - PeriodDate.builder() - .start(Date.from(Instant.parse("2026-01-20T00:00:00.00Z"))) - .build() - ) - .build() + val mtbFile = + PatientRecord.builder() + .patient(Patient.builder().id("123").build()) + .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) + .episodesOfCare( + listOf( + MtbEpisodeOfCare.builder() + .id("1") + .patient(Reference.builder().id("123").build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2026-01-20T00:00:00.00Z"))) + .build() + ) + .build() + ) + ) + .followUps( + listOf( + FollowUp.builder() + .patient(Reference.builder().id("123").build()) + .date(Date.from(Instant.parse("2026-05-21T00:00:00.00Z"))) + .lastContactDate(Date.from(Instant.parse("2026-05-21T00:00:00.00Z"))) + .build() + ) + ) + .build() + + this.requestProcessor.processMtbFile(mtbFile) + + val requestCaptor = argumentCaptor<Request>() + verify(requestService, times(1)).save(requestCaptor.capture()) + assertThat(requestCaptor.firstValue).isNotNull + assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) + assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.FOLLOWUP) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(0) + assertThat(requestCaptor.firstValue.expectedFollowupCount).isEqualTo(1) + + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) + + val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() + verify(sender, times(1)).send(sendRequestCaptor.capture()) + assertThat(sendRequestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.FOLLOWUP) + } + + @Test + fun testShouldSendAdditionMtbFileAfterFollowUpWasSent() { + + val lastRequests = + listOf( + Request( + 1L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.INITIAL, + RequestStatus.ERROR, + Tan.empty(), + Instant.parse("2026-02-10T09:00:00Z"), + submissionAccepted = false, + followupCount = 0 + ), + Request( + 2L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.INITIAL, + RequestStatus.WARNING, + Tan.empty(), + Instant.parse("2026-02-11T09:00:00Z"), + submissionAccepted = true, + followupCount = 0 + ), + Request( + 3L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.FOLLOWUP, + RequestStatus.WARNING, + Tan.empty(), + Instant.parse("2026-05-20T09:00:00Z"), + submissionAccepted = false, + followupCount = 1 ) ) - .followUps( - listOf( - FollowUp.builder() - .patient(Reference.builder().id("123").build()) - .date(Date.from(Instant.parse("2026-05-20T00:00:00.00Z"))) - .lastContactDate(Date.from(Instant.parse("2026-05-20T00:00:00.00Z"))) - .build() + + doAnswer { lastRequests } + .whenever(requestService) + .allRequestsByPatientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] as String } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) + + doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } + .whenever(sender) + .send(any<DnpmV2MtbFileRequest>()) + + whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) + + requestProcessor = + RequestProcessor( + pseudonymizeService, + transformationService, + sender, + requestService, + jsonMapper, + applicationEventPublisher, + AppConfigProperties(postInitialSubmissionBlock = true), + consentProcessor, + ) + + val mtbFile = + PatientRecord.builder() + .patient(Patient.builder().id("123").build()) + .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) + .episodesOfCare( + listOf( + MtbEpisodeOfCare.builder() + .id("1") + .patient(Reference.builder().id("123").build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2026-01-20T00:00:00.00Z"))) + .build() + ) + .build() + ) + ) + .followUps( + listOf( + FollowUp.builder() + .patient(Reference.builder().id("123").build()) + .date(Date.from(Instant.parse("2026-05-20T00:00:00.00Z"))) + .lastContactDate(Date.from(Instant.parse("2026-05-20T00:00:00.00Z"))) + .build() + ) + ) + .build() + + this.requestProcessor.processMtbFile(mtbFile) + + val requestCaptor = argumentCaptor<Request>() + verify(requestService, times(1)).save(requestCaptor.capture()) + assertThat(requestCaptor.firstValue).isNotNull + assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) + assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.ADDITION) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(1) + assertThat(requestCaptor.firstValue.expectedFollowupCount).isEqualTo(1) + + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) + + val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() + verify(sender, times(1)).send(sendRequestCaptor.capture()) + assertThat(sendRequestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.ADDITION) + } + + @Test + fun testShouldSendAdditionalFollowUpMtbFileAfterAdditionWasSentOnSameDay() { + + val lastRequests = + listOf( + Request( + 1L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.INITIAL, + RequestStatus.ERROR, + Tan.empty(), + Instant.parse("2026-02-10T09:00:00Z"), + submissionAccepted = false, + followupCount = 0 + ), + Request( + 2L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.INITIAL, + RequestStatus.WARNING, + Tan.empty(), + Instant.parse("2026-02-11T09:00:00Z"), + submissionAccepted = true, + followupCount = 0 + ), + Request( + 3L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.FOLLOWUP, + RequestStatus.WARNING, + Tan.empty(), + Instant.parse("2026-05-20T09:00:00Z"), + submissionAccepted = false, + followupCount = 1 + ), + Request( + 4L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.ADDITION, + RequestStatus.WARNING, + Tan.empty(), + Instant.parse("2026-06-11T05:41:00Z"), + submissionAccepted = false, + followupCount = 1 ) ) - .build() - this.requestProcessor.processMtbFile(mtbFile) + doAnswer { lastRequests } + .whenever(requestService) + .allRequestsByPatientPseudonym(anyValueClass()) - val requestCaptor = argumentCaptor<Request>() - verify(requestService, times(1)).save(requestCaptor.capture()) - assertThat(requestCaptor.firstValue).isNotNull - assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) - assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.ADDITION) + doAnswer { it.arguments[0] as String } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) - val eventCaptor = argumentCaptor<ResponseEvent>() - verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) - assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) + + doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } + .whenever(sender) + .send(any<DnpmV2MtbFileRequest>()) + + whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) + + requestProcessor = + RequestProcessor( + pseudonymizeService, + transformationService, + sender, + requestService, + jsonMapper, + applicationEventPublisher, + AppConfigProperties(postInitialSubmissionBlock = true), + consentProcessor, + ) + + val mtbFile = + PatientRecord.builder() + .patient(Patient.builder().id("123").build()) + .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) + .episodesOfCare( + listOf( + MtbEpisodeOfCare.builder() + .id("1") + .patient(Reference.builder().id("123").build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2026-01-20T00:00:00.00Z"))) + .build() + ) + .build() + ) + ) + .followUps( + listOf( + FollowUp.builder() + .patient(Reference.builder().id("123").build()) + .date(Date.from(Instant.parse("2026-05-20T00:00:00.00Z"))) + .lastContactDate(Date.from(Instant.parse("2026-05-20T00:00:00.00Z"))) + .build(), + FollowUp.builder() + .patient(Reference.builder().id("123").build()) + .date(Date.from(Instant.parse("2026-06-11T00:00:00.00Z"))) + .lastContactDate(Date.from(Instant.parse("2026-06-11T00:00:00.00Z"))) + .build() + ) + ) + .build() + + this.requestProcessor.processMtbFile(mtbFile) + + val requestCaptor = argumentCaptor<Request>() + verify(requestService, times(1)).save(requestCaptor.capture()) + assertThat(requestCaptor.firstValue).isNotNull + assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) + assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.FOLLOWUP) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(1) + assertThat(requestCaptor.firstValue.expectedFollowupCount).isEqualTo(2) + + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) + + val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() + verify(sender, times(1)).send(sendRequestCaptor.capture()) + assertThat(sendRequestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.FOLLOWUP) + } + + @Test + fun testShouldSendAdditionMtbFileWithoutNewHigherFollowUpsCount() { + + val lastRequests = + listOf( + Request( + 1L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.INITIAL, + RequestStatus.ERROR, + Tan.empty(), + Instant.parse("2026-02-10T09:00:00Z"), + submissionAccepted = false, + followupCount = -1 + ), + Request( + 2L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.INITIAL, + RequestStatus.WARNING, + Tan.empty(), + Instant.parse("2026-02-11T09:00:00Z"), + submissionAccepted = true, + followupCount = -1 + ), + Request( + 3L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.FOLLOWUP, + RequestStatus.WARNING, + Tan.empty(), + Instant.parse("2026-06-11T05:41:00Z"), + submissionAccepted = false, + followupCount = 1 + ) + ) + + doAnswer { lastRequests } + .whenever(requestService) + .allRequestsByPatientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] as String } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) + + doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } + .whenever(sender) + .send(any<DnpmV2MtbFileRequest>()) + + whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) + + requestProcessor = + RequestProcessor( + pseudonymizeService, + transformationService, + sender, + requestService, + jsonMapper, + applicationEventPublisher, + AppConfigProperties(postInitialSubmissionBlock = true), + consentProcessor, + ) + + val mtbFile = + PatientRecord.builder() + .patient(Patient.builder().id("123").build()) + .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) + .episodesOfCare( + listOf( + MtbEpisodeOfCare.builder() + .id("1") + .patient(Reference.builder().id("123").build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2026-01-20T00:00:00.00Z"))) + .build() + ) + .build() + ) + ) + .followUps( + listOf( + FollowUp.builder() + .patient(Reference.builder().id("123").build()) + .date(Date.from(Instant.parse("2026-06-11T00:00:00.00Z"))) + .lastContactDate(Date.from(Instant.parse("2026-06-11T00:00:00.00Z"))) + .build() + ) + ) + .build() + + this.requestProcessor.processMtbFile(mtbFile) + + val requestCaptor = argumentCaptor<Request>() + verify(requestService, times(1)).save(requestCaptor.capture()) + assertThat(requestCaptor.firstValue).isNotNull + assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) + assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.ADDITION) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(1) + assertThat(requestCaptor.firstValue.expectedFollowupCount).isEqualTo(1) + + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) + + val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() + verify(sender, times(1)).send(sendRequestCaptor.capture()) + assertThat(sendRequestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.ADDITION) + } + + @Test + fun testShouldSendInitialMtbFileContainingFollowUpsWithoutSuccessfulInitialSubmission() { + + val lastRequests = + listOf( + Request( + 1L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.INITIAL, + RequestStatus.ERROR, + Tan.empty(), + Instant.parse("2026-02-10T09:00:00Z"), + submissionAccepted = false, + followupCount = 0 + ) + ) + + doAnswer { lastRequests } + .whenever(requestService) + .allRequestsByPatientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] as String } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) + + doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } + .whenever(sender) + .send(any<DnpmV2MtbFileRequest>()) + + whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) + + requestProcessor = + RequestProcessor( + pseudonymizeService, + transformationService, + sender, + requestService, + jsonMapper, + applicationEventPublisher, + AppConfigProperties(postInitialSubmissionBlock = true), + consentProcessor, + ) + + val mtbFile = + PatientRecord.builder() + .patient(Patient.builder().id("123").build()) + .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) + .episodesOfCare( + listOf( + MtbEpisodeOfCare.builder() + .id("1") + .patient(Reference.builder().id("123").build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2026-01-20T00:00:00.00Z"))) + .build() + ) + .build() + ) + ) + .followUps( + listOf( + FollowUp.builder() + .patient(Reference.builder().id("123").build()) + .date(Date.from(Instant.parse("2026-06-11T00:00:00.00Z"))) + .lastContactDate(Date.from(Instant.parse("2026-06-11T00:00:00.00Z"))) + .build() + ) + ) + .build() + + this.requestProcessor.processMtbFile(mtbFile) + + val requestCaptor = argumentCaptor<Request>() + verify(requestService, times(1)).save(requestCaptor.capture()) + assertThat(requestCaptor.firstValue).isNotNull + assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) + assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.INITIAL) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(0) + assertThat(requestCaptor.firstValue.expectedFollowupCount).isEqualTo(1) + + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) + + val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() + verify(sender, times(1)).send(sendRequestCaptor.capture()) + assertThat(sendRequestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.INITIAL) + } - val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() - verify(sender, times(1)).send(sendRequestCaptor.capture()) - assertThat(sendRequestCaptor.firstValue.content.metadata.type).isEqualTo(MvhSubmissionType.ADDITION) + @Test + fun testShouldSendAdditionMtbFileWithoutFollowUpsAndInitialNegativeCount() { + + val lastRequests = + listOf( + Request( + 1L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.INITIAL, + RequestStatus.ERROR, + Tan.empty(), + Instant.parse("2026-02-10T09:00:00Z"), + submissionAccepted = false, + followupCount = -1 + ), + Request( + 2L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("initial"), + RequestType.MTB_FILE, + SubmissionType.INITIAL, + RequestStatus.WARNING, + Tan.empty(), + Instant.parse("2026-02-11T09:00:00Z"), + submissionAccepted = true, + followupCount = -1 + ), + ) + + doAnswer { lastRequests } + .whenever(requestService) + .allRequestsByPatientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] as String } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<PatientRecord>()) + + doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } + .whenever(sender) + .send(any<DnpmV2MtbFileRequest>()) + + whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) + + requestProcessor = + RequestProcessor( + pseudonymizeService, + transformationService, + sender, + requestService, + jsonMapper, + applicationEventPublisher, + AppConfigProperties(postInitialSubmissionBlock = true), + consentProcessor, + ) + + val mtbFile = + PatientRecord.builder() + .patient(Patient.builder().id("123").build()) + .metadata(MvhMetadata.builder().type(MvhSubmissionType.INITIAL).build()) + .episodesOfCare( + listOf( + MtbEpisodeOfCare.builder() + .id("1") + .patient(Reference.builder().id("123").build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2026-01-20T00:00:00.00Z"))) + .build() + ) + .build() + ) + ) + .build() + + this.requestProcessor.processMtbFile(mtbFile) + + val requestCaptor = argumentCaptor<Request>() + verify(requestService, times(1)).save(requestCaptor.capture()) + assertThat(requestCaptor.firstValue).isNotNull + assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) + assertThat(requestCaptor.firstValue.submissionType).isEqualTo(SubmissionType.ADDITION) + assertThat(requestCaptor.firstValue.followupCount).isEqualTo(-1) + assertThat(requestCaptor.firstValue.expectedFollowupCount).isEqualTo(0) + + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) + + val sendRequestCaptor = argumentCaptor<DnpmV2MtbFileRequest>() + verify(sender, times(1)).send(sendRequestCaptor.capture()) + assertThat(sendRequestCaptor.firstValue.content.metadata?.type).isEqualTo(MvhSubmissionType.ADDITION) + } } @Test fun shouldCatchExceptionsWhenProcessingMtbFileAndSaveError() { - val invalidMtbFile = Mtb.builder().build() + val invalidMtbFile = PatientRecord.builder().build() val success = this.requestProcessor.processMtbFile(invalidMtbFile) diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt index 441f3a9..c7a0af7 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt @@ -32,6 +32,7 @@ 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.Arguments import org.junit.jupiter.params.provider.MethodSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension @@ -115,6 +116,27 @@ class ResponseProcessorTest { assertThat(captor.firstValue.status).isEqualTo(requestStatus) } + @ParameterizedTest + @MethodSource("requestFollowUpCount") + fun shoulUpdateFollowUpCountFromExpectedFollowUpCountOnSuccess(requestStatus: RequestStatus, followUpCount: Int, expectedFollowUpCount: Int) { + doAnswer { + testRequest.followupCount = followUpCount + testRequest.expectedFollowupCount = expectedFollowUpCount + Optional.of(testRequest) + }.whenever(requestService).findByUuid(anyValueClass()) + + val event = + ResponseEvent(RequestId("TestID1234"), Instant.parse("2023-09-09T00:00:00Z"), requestStatus) + + this.responseProcessor.handleResponseEvent(event) + + val captor = argumentCaptor<Request>() + verify(requestService, times(1)).save(captor.capture()) + assertThat(captor.firstValue).isNotNull + assertThat(captor.firstValue.status).isEqualTo(requestStatus) + assertThat(captor.firstValue.followupCount).isEqualTo(expectedFollowUpCount) + } + companion object { @JvmStatic @@ -126,5 +148,16 @@ class ResponseProcessorTest { RequestStatus.DUPLICATION, ) } + + @JvmStatic + fun requestFollowUpCount(): Set<Arguments> { + return setOf( + Arguments.of(RequestStatus.SUCCESS, 1, 2), + Arguments.of(RequestStatus.WARNING, 1, 2), + Arguments.of(RequestStatus.ERROR, 1, 1), + Arguments.of(RequestStatus.DUPLICATION, 1, 1), + Arguments.of(RequestStatus.NO_CONSENT, 1, 1), + ) + } } } 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 66e45f8..cbca4f7 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt @@ -24,7 +24,15 @@ import ca.uhn.fhir.context.FhirContext import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.node.ObjectNode import dev.dnpm.etl.processor.config.JacksonConfig -import dev.pcvolkmer.mv64e.mtb.* +import dev.pcvolkmer.mv64e.model.Coding +import dev.pcvolkmer.mv64e.model.ConsentProvisionType +import dev.pcvolkmer.mv64e.model.ModelProjectConsentPurpose +import dev.pcvolkmer.mv64e.model.MtbDiagnosis +import dev.pcvolkmer.mv64e.model.MvhMetadata +import dev.pcvolkmer.mv64e.model.MvhMetadataModelProjectConsent +import dev.pcvolkmer.mv64e.model.MvhMetadataModelProjectConsentProvisionsInner +import dev.pcvolkmer.mv64e.model.PatientRecord +import dev.pcvolkmer.mv64e.model.ResearchConsent import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -49,7 +57,7 @@ class TransformationServiceTest { @Test fun shouldTransformMtbFile() { val mtbFile = - Mtb.builder() + PatientRecord.builder() .diagnoses( listOf( MtbDiagnosis.builder() @@ -63,13 +71,13 @@ class TransformationServiceTest { val actual = this.service.transform(mtbFile) assertThat(actual).isNotNull - assertThat(actual.diagnoses[0].code.version).isEqualTo("2014") + assertThat(actual.diagnoses!![0].code.version).isEqualTo("2014") } @Test fun shouldOnlyTransformGivenValues() { val mtbFile = - Mtb.builder() + PatientRecord.builder() .diagnoses( listOf( MtbDiagnosis.builder() @@ -87,16 +95,16 @@ class TransformationServiceTest { val actual = this.service.transform(mtbFile) assertThat(actual).isNotNull - assertThat(actual.diagnoses[0].code.code).isEqualTo("F79.9") - assertThat(actual.diagnoses[0].code.version).isEqualTo("2014") - assertThat(actual.diagnoses[1].code.code).isEqualTo("F79.8") - assertThat(actual.diagnoses[1].code.version).isEqualTo("2019") + assertThat(actual.diagnoses!![0].code.code).isEqualTo("F79.9") + assertThat(actual.diagnoses!![0].code.version).isEqualTo("2014") + assertThat(actual.diagnoses!![1].code.code).isEqualTo("F79.8") + assertThat(actual.diagnoses!![1].code.version).isEqualTo("2019") } @Test fun shouldTransformConsentValues() { val mtbFile = - Mtb.builder() + PatientRecord.builder() .diagnoses( listOf( MtbDiagnosis.builder() @@ -114,35 +122,35 @@ class TransformationServiceTest { val actual = this.service.transform(mtbFile) assertThat(actual).isNotNull - assertThat(actual.diagnoses[0].code.code).isEqualTo("F79.9") - assertThat(actual.diagnoses[0].code.version).isEqualTo("2014") - assertThat(actual.diagnoses[1].code.code).isEqualTo("F79.8") - assertThat(actual.diagnoses[1].code.version).isEqualTo("2019") + assertThat(actual.diagnoses!![0].code.code).isEqualTo("F79.9") + assertThat(actual.diagnoses!![0].code.version).isEqualTo("2014") + assertThat(actual.diagnoses!![1].code.code).isEqualTo("F79.8") + assertThat(actual.diagnoses!![1].code.version).isEqualTo("2019") } @Test fun shouldTransformConsent() { - val mvhMetadata = MvhMetadata.builder().transferTan("transfertan12345").build() + val mvhMetadata = MvhMetadata.builder().transferTAN("transfertan12345").build() assertThat(mvhMetadata).isNotNull mvhMetadata.modelProjectConsent = - ModelProjectConsent.builder() + MvhMetadataModelProjectConsent.builder() .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))) .version("1") .provisions( listOf( - Provision.builder() - .type(ConsentProvision.PERMIT) + MvhMetadataModelProjectConsentProvisionsInner.builder() + .type(ConsentProvisionType.PERMIT) .purpose(ModelProjectConsentPurpose.SEQUENCING) .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))) .build(), - Provision.builder() - .type(ConsentProvision.PERMIT) + MvhMetadataModelProjectConsentProvisionsInner.builder() + .type(ConsentProvisionType.PERMIT) .purpose(ModelProjectConsentPurpose.REIDENTIFICATION) .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))) .build(), - Provision.builder() - .type(ConsentProvision.DENY) + MvhMetadataModelProjectConsentProvisionsInner.builder() + .type(ConsentProvisionType.DENY) .purpose(ModelProjectConsentPurpose.CASE_IDENTIFICATION) .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))) .build(), @@ -158,11 +166,11 @@ class TransformationServiceTest { ) mvhMetadata.researchConsents = mutableListOf() - mvhMetadata.researchConsents.add(MvhMetadata.ResearchConsent.from(jsonNode)) + mvhMetadata.researchConsents?.add(ResearchConsent.from(jsonNode)) - val mtbFile = Mtb.builder().metadata(mvhMetadata).build() + val mtbFile = PatientRecord.builder().metadata(mvhMetadata).build() val transformed = service.transform(mtbFile) - assertThat(transformed.metadata.modelProjectConsent.date).isNotNull + assertThat(transformed.metadata?.modelProjectConsent?.date).isNotNull } } |
