diff options
| author | Paul-Christian Volkmer | 2025-11-11 09:36:17 +0100 |
|---|---|---|
| committer | GitHub | 2025-11-11 09:36:17 +0100 |
| commit | 8c3143081f5f535671bc7b0c7d98302549b9a1da (patch) | |
| tree | 1402d033ef7b13ab6befddf2b8d1f385f79d410b /src/test/kotlin | |
| parent | 76a16e22bb9fba21d02b0584ec83e046196da045 (diff) | |
chore: use spotless for kotlin code (#191)
Diffstat (limited to 'src/test/kotlin')
20 files changed, 2390 insertions, 2379 deletions
diff --git a/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt index 5a86a29..1140425 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/consent/ConsentProcessorTest.kt @@ -5,6 +5,7 @@ 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.services.ConsentProcessor +import java.util.* import org.assertj.core.api.Assertions.assertThat import org.hl7.fhir.r4.model.Bundle import org.hl7.fhir.r4.model.Consent @@ -14,45 +15,46 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.CsvSource import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension -import java.util.* @ExtendWith(MockitoExtension::class) class ConsentProcessorTest { - lateinit var consentProcessor: ConsentProcessor + lateinit var consentProcessor: ConsentProcessor - val objectMapper = ObjectMapper() - val fhirContext = FhirContext.forR4() + val objectMapper = ObjectMapper() + val fhirContext = FhirContext.forR4() - @BeforeEach - fun setup( - @Mock consentService: IConsentService - ) { - val appConfigProperties = AppConfigProperties() - val gIcsConfigProperties = GIcsConfigProperties("http://localhost") + @BeforeEach + fun setup(@Mock consentService: IConsentService) { + val appConfigProperties = AppConfigProperties() + val gIcsConfigProperties = GIcsConfigProperties("http://localhost") - this.consentProcessor = ConsentProcessor( + this.consentProcessor = + ConsentProcessor( appConfigProperties, gIcsConfigProperties, objectMapper, fhirContext, - consentService + 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) + } + + @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(bundle).isInstanceOf(Bundle::class.java) - - val actual = consentProcessor.getProvisionTypeByPolicyCode(bundle as Bundle, Date(), ConsentDomain.BROAD_CONSENT) - assertThat(actual).isEqualTo(Consent.ConsentProvisionType.valueOf(expected.uppercase())) - } -}
\ No newline at end of file + 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 adbec2f..85a8b3e 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/consent/Dnpm21BasedConsentEvaluatorTest.kt @@ -21,6 +21,8 @@ package dev.dnpm.etl.processor.consent import dev.dnpm.etl.processor.ArgProvider import dev.pcvolkmer.mv64e.mtb.* +import java.time.Instant +import java.util.* import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested @@ -32,256 +34,269 @@ import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.whenever -import java.time.Instant -import java.util.* @ExtendWith(MockitoExtension::class) class Dnpm21BasedConsentEvaluatorTest { - @Nested - inner class WithGicsConsentEnabled { + @Nested + inner class WithGicsConsentEnabled { - lateinit var consentService: GicsConsentService - lateinit var consentEvaluator: ConsentEvaluator + lateinit var consentService: GicsConsentService + lateinit var consentEvaluator: ConsentEvaluator - @BeforeEach - fun setUp( - @Mock consentService: GicsConsentService - ) { - this.consentService = consentService - this.consentEvaluator = ConsentEvaluator(consentService) - } + @BeforeEach + fun setUp(@Mock consentService: GicsConsentService) { + this.consentService = consentService + this.consentEvaluator = ConsentEvaluator(consentService) + } - @ParameterizedTest - @ArgumentsSource(WithGicsMtbFileProvider::class) - fun test( - mtbFile: Mtb, - ttpConsentStatus: TtpConsentStatus, - expectedConsentEvaluation: ConsentEvaluation - ) { - whenever(consentService.getTtpBroadConsentStatus(anyString())).thenReturn( - ttpConsentStatus - ) - assertThat(consentEvaluator.check(mtbFile)).isEqualTo(expectedConsentEvaluation) - } + @ParameterizedTest + @ArgumentsSource(WithGicsMtbFileProvider::class) + fun test( + mtbFile: Mtb, + ttpConsentStatus: TtpConsentStatus, + expectedConsentEvaluation: ConsentEvaluation, + ) { + whenever(consentService.getTtpBroadConsentStatus(anyString())).thenReturn(ttpConsentStatus) + assertThat(consentEvaluator.check(mtbFile)).isEqualTo(expectedConsentEvaluation) } + } - @Nested - inner class WithFileConsentOnly { + @Nested + inner class WithFileConsentOnly { - lateinit var consentService: MtbFileConsentService - lateinit var consentEvaluator: ConsentEvaluator + lateinit var consentService: MtbFileConsentService + lateinit var consentEvaluator: ConsentEvaluator - @BeforeEach - fun setUp() { - this.consentService = MtbFileConsentService() - this.consentEvaluator = ConsentEvaluator(consentService) - } + @BeforeEach + fun setUp() { + this.consentService = MtbFileConsentService() + this.consentEvaluator = ConsentEvaluator(consentService) + } - @ParameterizedTest - @ArgumentsSource(MtbFileProvider::class) - fun test(mtbFile: Mtb, expectedConsentEvaluation: ConsentEvaluation) { - assertThat(consentEvaluator.check(mtbFile)).isEqualTo(expectedConsentEvaluation) - } + @ParameterizedTest + @ArgumentsSource(MtbFileProvider::class) + fun test(mtbFile: Mtb, expectedConsentEvaluation: ConsentEvaluation) { + assertThat(consentEvaluator.check(mtbFile)).isEqualTo(expectedConsentEvaluation) } + } - // Util classes + // Util classes - class WithGicsMtbFileProvider : ArgProvider( - // Has file ModelProjectConsent and broad consent => consent given - Arguments.of( - buildMtb(ConsentProvision.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), - 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), - 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), - 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), - 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), - 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), - 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), - TtpConsentStatus.FAILED_TO_ASK, - ConsentEvaluation(TtpConsentStatus.FAILED_TO_ASK, false) - ), - // File ModelProjectConsent rejected and broad consent => consent given - Arguments.of( - buildMtb(ConsentProvision.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), - 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), - 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), - 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), - 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), - 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), - 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), - TtpConsentStatus.FAILED_TO_ASK, - ConsentEvaluation(TtpConsentStatus.FAILED_TO_ASK, false) - ) - ) { + class WithGicsMtbFileProvider : + ArgProvider( + // Has file ModelProjectConsent and broad consent => consent given + Arguments.of( + buildMtb(ConsentProvision.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), + 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), + 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), + 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), + 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), + 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), + 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), + TtpConsentStatus.FAILED_TO_ASK, + ConsentEvaluation(TtpConsentStatus.FAILED_TO_ASK, false), + ), + // File ModelProjectConsent rejected and broad consent => consent given + Arguments.of( + buildMtb(ConsentProvision.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), + 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), + 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), + 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), + 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), + 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), + 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), + TtpConsentStatus.FAILED_TO_ASK, + ConsentEvaluation(TtpConsentStatus.FAILED_TO_ASK, false), + ), + ) { - companion object { - fun buildMtb(consentProvision: ConsentProvision): Mtb { - return Mtb.builder() - .patient( - Patient.builder().id("TEST_12345678") - .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))).gender( - GenderCoding.builder().code(GenderCodingCode.MALE).build() - ).build() - ) - .metadata( - MvhMetadata.builder().modelProjectConsent( - ModelProjectConsent.builder().provisions( + companion object { + fun buildMtb(consentProvision: ConsentProvision): Mtb { + return Mtb.builder() + .patient( + Patient.builder() + .id("TEST_12345678") + .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))) + .gender(GenderCoding.builder().code(GenderCodingCode.MALE).build()) + .build() + ) + .metadata( + MvhMetadata.builder() + .modelProjectConsent( + ModelProjectConsent.builder() + .provisions( listOf( - Provision.builder().date(Date()).type(consentProvision) - .purpose(ModelProjectConsentPurpose.SEQUENCING).build() + Provision.builder() + .date(Date()) + .type(consentProvision) + .purpose(ModelProjectConsentPurpose.SEQUENCING) + .build() ) - ).build() - ).build() - ) - .episodesOfCare( - listOf( - MtbEpisodeOfCare.builder().id("1") - .patient(Reference.builder().id("TEST_12345678").build()) - .build() - ) + ) + .build() ) .build() - } - } + ) + .episodesOfCare( + listOf( + MtbEpisodeOfCare.builder() + .id("1") + .patient(Reference.builder().id("TEST_12345678").build()) + .build() + ) + ) + .build() + } } + } - class MtbFileProvider : ArgProvider( - // Has file consent => consent given - Arguments.of( - buildMtb(ConsentProvision.PERMIT), - ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, true) - ), - // File consent rejected => no consent given - Arguments.of( - buildMtb(ConsentProvision.DENY), - ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false) - ), - // policy REIDENTIFICATION has no effect on ConsentEvaluation - Arguments.of( - buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvision.DENY), - ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false) - ), Arguments.of( - buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvision.PERMIT), - ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false) - ), - // policy CASE_IDENTIFICATION has no effect on ConsentEvaluation - Arguments.of( - buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvision.DENY), - ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false) - ), Arguments.of( - buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvision.PERMIT), - ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false) - ) - ) { + class MtbFileProvider : + ArgProvider( + // Has file consent => consent given + Arguments.of( + buildMtb(ConsentProvision.PERMIT), + ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, true), + ), + // File consent rejected => no consent given + Arguments.of( + buildMtb(ConsentProvision.DENY), + ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false), + ), + // policy REIDENTIFICATION has no effect on ConsentEvaluation + Arguments.of( + buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvision.DENY), + ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false), + ), + Arguments.of( + buildMtb(ModelProjectConsentPurpose.REIDENTIFICATION, ConsentProvision.PERMIT), + ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false), + ), + // policy CASE_IDENTIFICATION has no effect on ConsentEvaluation + Arguments.of( + buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvision.DENY), + ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false), + ), + Arguments.of( + buildMtb(ModelProjectConsentPurpose.CASE_IDENTIFICATION, ConsentProvision.PERMIT), + ConsentEvaluation(TtpConsentStatus.UNKNOWN_CHECK_FILE, false), + ), + ) { - companion object { - fun buildMtb(consentProvision: ConsentProvision): Mtb { - return buildMtb(ModelProjectConsentPurpose.SEQUENCING, consentProvision) - } + companion object { + fun buildMtb(consentProvision: ConsentProvision): Mtb { + return buildMtb(ModelProjectConsentPurpose.SEQUENCING, consentProvision) + } - fun buildMtb( - policy: ModelProjectConsentPurpose, - consentProvision: ConsentProvision - ): Mtb { - return Mtb.builder() - .patient( - Patient.builder().id("TEST_12345678") - .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))).gender( - GenderCoding.builder().code(GenderCodingCode.MALE).build() - ).build() - ) - .metadata( - MvhMetadata.builder().modelProjectConsent( - ModelProjectConsent.builder().provisions( + fun buildMtb(policy: ModelProjectConsentPurpose, consentProvision: ConsentProvision): Mtb { + return Mtb.builder() + .patient( + Patient.builder() + .id("TEST_12345678") + .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))) + .gender(GenderCoding.builder().code(GenderCodingCode.MALE).build()) + .build() + ) + .metadata( + MvhMetadata.builder() + .modelProjectConsent( + ModelProjectConsent.builder() + .provisions( listOf( - Provision.builder().date(Date()).type(consentProvision) - .purpose(policy).build() + Provision.builder() + .date(Date()) + .type(consentProvision) + .purpose(policy) + .build() ) - ).build() - ).build() - ) - .episodesOfCare( - listOf( - MtbEpisodeOfCare.builder().id("1") - .patient(Reference.builder().id("TEST_12345678").build()) - .build() - ) + ) + .build() ) .build() - } - } + ) + .episodesOfCare( + listOf( + MtbEpisodeOfCare.builder() + .id("1") + .patient(Reference.builder().id("TEST_12345678").build()) + .build() + ) + ) + .build() + } } - + } } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt b/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt index 2dfb1e1..495bf38 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/helpers.kt @@ -19,13 +19,12 @@ package dev.dnpm.etl.processor +import java.util.stream.Stream import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.ArgumentsProvider -import java.util.stream.Stream open class ArgProvider(vararg val data: Arguments) : ArgumentsProvider { - override fun provideArguments( - context: ExtensionContext? - ): Stream<out Arguments> = Stream.of(*data) + override fun provideArguments(context: ExtensionContext?): Stream<out Arguments> = + Stream.of(*data) } 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 7f07766..da05fba 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/input/KafkaInputListenerTest.kt @@ -26,6 +26,7 @@ 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 java.util.* import org.apache.kafka.clients.consumer.ConsumerRecord import org.apache.kafka.common.header.internals.RecordHeader import org.apache.kafka.common.header.internals.RecordHeaders @@ -36,267 +37,241 @@ import org.junit.jupiter.api.extension.ExtendWith import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.* -import java.util.* @ExtendWith(MockitoExtension::class) class KafkaInputListenerTest { - private lateinit var requestProcessor: RequestProcessor - private lateinit var consentEvaluator: ConsentEvaluator - private lateinit var objectMapper: ObjectMapper + private lateinit var requestProcessor: RequestProcessor + private lateinit var consentEvaluator: ConsentEvaluator + private lateinit var objectMapper: ObjectMapper - private lateinit var kafkaInputListener: KafkaInputListener + private lateinit var kafkaInputListener: KafkaInputListener - @BeforeEach - fun setup( - @Mock requestProcessor: RequestProcessor, - @Mock consentEvaluator: ConsentEvaluator, - ) { - this.requestProcessor = requestProcessor - this.consentEvaluator = consentEvaluator - this.objectMapper = ObjectMapper() + @BeforeEach + fun setup( + @Mock requestProcessor: RequestProcessor, + @Mock consentEvaluator: ConsentEvaluator, + ) { + this.requestProcessor = requestProcessor + this.consentEvaluator = consentEvaluator + this.objectMapper = ObjectMapper() - this.kafkaInputListener = KafkaInputListener(requestProcessor, consentEvaluator, objectMapper) - } + this.kafkaInputListener = KafkaInputListener(requestProcessor, consentEvaluator, objectMapper) + } - @Test - fun shouldProcessMtbFileRequest() { - whenever(consentEvaluator.check(any())).thenReturn( - ConsentEvaluation( - TtpConsentStatus.BROAD_CONSENT_GIVEN, - true - ) - ) + @Test + fun shouldProcessMtbFileRequest() { + whenever(consentEvaluator.check(any())) + .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true)) - val mtbFile = Mtb.builder() + val mtbFile = + Mtb.builder() .patient(Patient.builder().id("DUMMY_12345678").build()) .metadata( - MvhMetadata - .builder() + MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent - .builder() + ModelProjectConsent.builder() .provisions( listOf( - Provision.builder().type(ConsentProvision.PERMIT) - .purpose(ModelProjectConsentPurpose.SEQUENCING).build() + Provision.builder() + .type(ConsentProvision.PERMIT) + .purpose(ModelProjectConsentPurpose.SEQUENCING) + .build() ) - ).build() + ) + .build() ) .build() ) .build() - kafkaInputListener.onMessage( - ConsumerRecord( - "testtopic", - 0, - 0, - "", - this.objectMapper.writeValueAsString(mtbFile) - ) - ) + kafkaInputListener.onMessage( + ConsumerRecord("testtopic", 0, 0, "", this.objectMapper.writeValueAsString(mtbFile)) + ) - verify(requestProcessor, times(1)).processMtbFile(any<Mtb>()) - } + verify(requestProcessor, times(1)).processMtbFile(any<Mtb>()) + } - @Test - fun shouldProcessDeleteRequest() { - whenever(consentEvaluator.check(any())).thenReturn( - ConsentEvaluation( - TtpConsentStatus.BROAD_CONSENT_GIVEN, - false - ) - ) + @Test + fun shouldProcessDeleteRequest() { + whenever(consentEvaluator.check(any())) + .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, false)) - val mtbFile = Mtb.builder() + val mtbFile = + Mtb.builder() .patient(Patient.builder().id("DUMMY_12345678").build()) .metadata( - MvhMetadata - .builder() + MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent - .builder() + ModelProjectConsent.builder() .provisions( listOf( - Provision.builder().type(ConsentProvision.DENY) - .purpose(ModelProjectConsentPurpose.SEQUENCING).build() + Provision.builder() + .type(ConsentProvision.DENY) + .purpose(ModelProjectConsentPurpose.SEQUENCING) + .build() ) - ).build() + ) + .build() ) .build() ) .build() - kafkaInputListener.onMessage( - ConsumerRecord( - "testtopic", - 0, - 0, - "", - this.objectMapper.writeValueAsString(mtbFile) - ) - ) + kafkaInputListener.onMessage( + ConsumerRecord("testtopic", 0, 0, "", this.objectMapper.writeValueAsString(mtbFile)) + ) - verify(requestProcessor, times(1)).processDeletion( - anyValueClass(), - eq(TtpConsentStatus.UNKNOWN_CHECK_FILE) - ) - } + verify(requestProcessor, times(1)) + .processDeletion(anyValueClass(), eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)) + } - @Test - fun shouldProcessMtbFileRequestWithExistingRequestId() { - whenever(consentEvaluator.check(any())).thenReturn( - ConsentEvaluation( - TtpConsentStatus.BROAD_CONSENT_GIVEN, - true - ) - ) + @Test + fun shouldProcessMtbFileRequestWithExistingRequestId() { + whenever(consentEvaluator.check(any())) + .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true)) - val mtbFile = Mtb.builder() + val mtbFile = + Mtb.builder() .patient(Patient.builder().id("DUMMY_12345678").build()) .metadata( - MvhMetadata - .builder() + MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent - .builder() + ModelProjectConsent.builder() .provisions( listOf( - Provision.builder().type(ConsentProvision.PERMIT) - .purpose(ModelProjectConsentPurpose.SEQUENCING).build() + Provision.builder() + .type(ConsentProvision.PERMIT) + .purpose(ModelProjectConsentPurpose.SEQUENCING) + .build() ) - ).build() + ) + .build() ) .build() ) .build() - val headers = RecordHeaders(listOf(RecordHeader("requestId", UUID.randomUUID().toString().toByteArray()))) - kafkaInputListener.onMessage( - ConsumerRecord( - "testtopic", - 0, - 0, - -1L, - TimestampType.NO_TIMESTAMP_TYPE, - -1, - -1, - "", - this.objectMapper.writeValueAsString(mtbFile), - headers, - Optional.empty() - ) + val headers = + RecordHeaders(listOf(RecordHeader("requestId", UUID.randomUUID().toString().toByteArray()))) + kafkaInputListener.onMessage( + ConsumerRecord( + "testtopic", + 0, + 0, + -1L, + TimestampType.NO_TIMESTAMP_TYPE, + -1, + -1, + "", + this.objectMapper.writeValueAsString(mtbFile), + headers, + Optional.empty(), ) + ) - verify(requestProcessor, times(1)).processMtbFile(any<Mtb>(), anyValueClass()) - } + verify(requestProcessor, times(1)).processMtbFile(any<Mtb>(), anyValueClass()) + } - @Test - fun shouldProcessDeleteRequestWithExistingRequestId() { - whenever(consentEvaluator.check(any())).thenReturn( - ConsentEvaluation( - TtpConsentStatus.BROAD_CONSENT_GIVEN, - false - ) - ) + @Test + fun shouldProcessDeleteRequestWithExistingRequestId() { + whenever(consentEvaluator.check(any())) + .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, false)) - val mtbFile = Mtb.builder() + val mtbFile = + Mtb.builder() .patient(Patient.builder().id("DUMMY_12345678").build()) .metadata( - MvhMetadata - .builder() + MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent - .builder() + ModelProjectConsent.builder() .provisions( listOf( - Provision.builder().type(ConsentProvision.DENY) - .purpose(ModelProjectConsentPurpose.SEQUENCING).build() + Provision.builder() + .type(ConsentProvision.DENY) + .purpose(ModelProjectConsentPurpose.SEQUENCING) + .build() ) - ).build() + ) + .build() ) .build() ) .build() - val headers = RecordHeaders(listOf(RecordHeader("requestId", UUID.randomUUID().toString().toByteArray()))) - kafkaInputListener.onMessage( - ConsumerRecord( - "testtopic", - 0, - 0, - -1L, - TimestampType.NO_TIMESTAMP_TYPE, - -1, - -1, - "", - this.objectMapper.writeValueAsString(mtbFile), - headers, - Optional.empty() - ) - ) - verify(requestProcessor, times(1)).processDeletion( - anyValueClass(), anyValueClass(), eq( - TtpConsentStatus.UNKNOWN_CHECK_FILE - ) + val headers = + RecordHeaders(listOf(RecordHeader("requestId", UUID.randomUUID().toString().toByteArray()))) + kafkaInputListener.onMessage( + ConsumerRecord( + "testtopic", + 0, + 0, + -1L, + TimestampType.NO_TIMESTAMP_TYPE, + -1, + -1, + "", + this.objectMapper.writeValueAsString(mtbFile), + headers, + Optional.empty(), ) - } + ) + verify(requestProcessor, times(1)) + .processDeletion(anyValueClass(), anyValueClass(), eq(TtpConsentStatus.UNKNOWN_CHECK_FILE)) + } - @Test - fun shouldProcessDnpmV2Request() { - whenever(consentEvaluator.check(any())).thenReturn( - ConsentEvaluation( - TtpConsentStatus.BROAD_CONSENT_GIVEN, - false - ) - ) + @Test + fun shouldProcessDnpmV2Request() { + whenever(consentEvaluator.check(any())) + .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, false)) - val mtbFile = Mtb.builder() + val mtbFile = + Mtb.builder() .patient(Patient.builder().id("DUMMY_12345678").build()) .metadata( - MvhMetadata - .builder() + MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent - .builder() + ModelProjectConsent.builder() .provisions( listOf( - Provision.builder().type(ConsentProvision.DENY) - .purpose(ModelProjectConsentPurpose.SEQUENCING).build() + Provision.builder() + .type(ConsentProvision.DENY) + .purpose(ModelProjectConsentPurpose.SEQUENCING) + .build() ) - ).build() + ) + .build() ) .build() ) .build() - val headers = RecordHeaders( + val headers = + RecordHeaders( listOf( RecordHeader("requestId", UUID.randomUUID().toString().toByteArray()), - RecordHeader("contentType", CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE.toByteArray()) - ) - ) - kafkaInputListener.onMessage( - ConsumerRecord( - "testtopic", - 0, - 0, - -1L, - TimestampType.NO_TIMESTAMP_TYPE, - -1, - -1, - "", - this.objectMapper.writeValueAsString(mtbFile), - headers, - Optional.empty() + RecordHeader( + "contentType", + CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE.toByteArray(), + ), ) ) - verify(requestProcessor, times(1)).processDeletion( - anyValueClass(), anyValueClass(), eq( - TtpConsentStatus.UNKNOWN_CHECK_FILE - ) + kafkaInputListener.onMessage( + ConsumerRecord( + "testtopic", + 0, + 0, + -1L, + TimestampType.NO_TIMESTAMP_TYPE, + -1, + -1, + "", + this.objectMapper.writeValueAsString(mtbFile), + headers, + Optional.empty(), ) - } - + ) + verify(requestProcessor, times(1)) + .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 ae9e4e2..95858f4 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/input/MtbFileRestControllerTest.kt @@ -27,6 +27,8 @@ 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 java.time.Instant +import java.util.* import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test @@ -46,181 +48,176 @@ import org.springframework.test.web.servlet.MockMvc import org.springframework.test.web.servlet.delete import org.springframework.test.web.servlet.post import org.springframework.test.web.servlet.setup.MockMvcBuilders -import java.time.Instant -import java.util.* @ExtendWith(MockitoExtension::class) class MtbFileRestControllerTest { - private val objectMapper = ObjectMapper() - - @Nested - inner class RequestsForDnpmDataModel21 { - - private lateinit var mockMvc: MockMvc - - private lateinit var requestProcessor: RequestProcessor - private lateinit var consentEvaluator: ConsentEvaluator - - @BeforeEach - fun setup( - @Mock requestProcessor: RequestProcessor, - @Mock consentEvaluator: ConsentEvaluator - ) { - this.requestProcessor = requestProcessor - this.consentEvaluator = consentEvaluator - val controller = MtbFileRestController( - requestProcessor, - consentEvaluator - ) - this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build() - } - - @Test - fun shouldRespondPostRequest() { - whenever(consentEvaluator.check(any())).thenReturn( - ConsentEvaluation( - TtpConsentStatus.BROAD_CONSENT_GIVEN, - true - ) - ) - - val mtbFileContent = - ClassPathResource("mv64e-mtb-fake-patient.json").inputStream.readAllBytes().toString(Charsets.UTF_8) - - mockMvc.post("/mtb") { - content = mtbFileContent - contentType = CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON - }.andExpect { - status { - isAccepted() - } - } - - verify(requestProcessor, times(1)).processMtbFile(any<Mtb>()) - } - - @ParameterizedTest - @ArgumentsSource(Dnpm21MtbFile::class) - fun shouldProcessPostRequest(mtb: Mtb, broadConsent: TtpConsentStatus, shouldProcess: String) { - whenever(consentEvaluator.check(any<Mtb>())).thenReturn( - ConsentEvaluation( - broadConsent, - shouldProcess == "process" - ) - ) - - mockMvc.post("/mtbfile") { - content = objectMapper.writeValueAsString(mtb) - contentType = CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON - }.andExpect { - status { - isAccepted() - } - } - - if (shouldProcess == "process") { - verify(requestProcessor, times(1)).processMtbFile(any<Mtb>()) - } else { - verify(requestProcessor, times(1)).processDeletion( - anyValueClass(), - org.mockito.kotlin.eq(broadConsent) - ) - } - } - - @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) - ) - verify(consentEvaluator, times(0)).check(any<Mtb>()) - } + private val objectMapper = ObjectMapper() + + @Nested + inner class RequestsForDnpmDataModel21 { + + private lateinit var mockMvc: MockMvc + + private lateinit var requestProcessor: RequestProcessor + private lateinit var consentEvaluator: ConsentEvaluator + + @BeforeEach + fun setup(@Mock requestProcessor: RequestProcessor, @Mock consentEvaluator: ConsentEvaluator) { + this.requestProcessor = requestProcessor + this.consentEvaluator = consentEvaluator + val controller = MtbFileRestController(requestProcessor, consentEvaluator) + this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build() + } + + @Test + fun shouldRespondPostRequest() { + whenever(consentEvaluator.check(any())) + .thenReturn(ConsentEvaluation(TtpConsentStatus.BROAD_CONSENT_GIVEN, true)) + + val mtbFileContent = + ClassPathResource("mv64e-mtb-fake-patient.json") + .inputStream + .readAllBytes() + .toString(Charsets.UTF_8) + + mockMvc + .post("/mtb") { + content = mtbFileContent + contentType = CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON + } + .andExpect { status { isAccepted() } } + + verify(requestProcessor, times(1)).processMtbFile(any<Mtb>()) } + + @ParameterizedTest + @ArgumentsSource(Dnpm21MtbFile::class) + fun shouldProcessPostRequest(mtb: Mtb, broadConsent: TtpConsentStatus, shouldProcess: String) { + whenever(consentEvaluator.check(any<Mtb>())) + .thenReturn(ConsentEvaluation(broadConsent, shouldProcess == "process")) + + mockMvc + .post("/mtbfile") { + content = objectMapper.writeValueAsString(mtb) + contentType = CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON + } + .andExpect { status { isAccepted() } } + + if (shouldProcess == "process") { + verify(requestProcessor, times(1)).processMtbFile(any<Mtb>()) + } else { + verify(requestProcessor, times(1)) + .processDeletion(anyValueClass(), org.mockito.kotlin.eq(broadConsent)) + } + } + + @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), + ) + verify(consentEvaluator, times(0)).check(any<Mtb>()) + } + } } -class Dnpm21MtbFile : ArgProvider( - // No Metadata and no broad consent => delete - Arguments.of( - buildMtb(null), - TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, - "delete" - ), - // No Metadata and broad consent given => process - Arguments.of( - buildMtb(null), - TtpConsentStatus.BROAD_CONSENT_GIVEN, - "process" - ), - // No model project consent and no broad consent => delete - Arguments.of( - buildMtb(MvhMetadata.builder().modelProjectConsent(ModelProjectConsent.builder().build()).build()), - TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, - "delete" - ), - // No model project consent and broad consent given => process - Arguments.of( - buildMtb(MvhMetadata.builder().modelProjectConsent(ModelProjectConsent.builder().build()).build()), - TtpConsentStatus.BROAD_CONSENT_GIVEN, - "process" - ), - // Model project consent given and no broad consent => process - Arguments.of( - buildMtb( - MvhMetadata.builder().modelProjectConsent( - ModelProjectConsent.builder().provisions( - listOf( - Provision.builder().date(Date()).type(ConsentProvision.PERMIT) - .purpose(ModelProjectConsentPurpose.SEQUENCING).build() - ) - ).build() - ).build() +class Dnpm21MtbFile : + ArgProvider( + // No Metadata and no broad consent => delete + Arguments.of(buildMtb(null), TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, "delete"), + // No Metadata and broad consent given => process + Arguments.of(buildMtb(null), TtpConsentStatus.BROAD_CONSENT_GIVEN, "process"), + // No model project consent and no broad consent => delete + Arguments.of( + buildMtb( + MvhMetadata.builder() + .modelProjectConsent(ModelProjectConsent.builder().build()) + .build() + ), + TtpConsentStatus.BROAD_CONSENT_MISSING_OR_REJECTED, + "delete", + ), + // No model project consent and broad consent given => process + Arguments.of( + buildMtb( + MvhMetadata.builder() + .modelProjectConsent(ModelProjectConsent.builder().build()) + .build() + ), + TtpConsentStatus.BROAD_CONSENT_GIVEN, + "process", ), - TtpConsentStatus.UNKNOWN_CHECK_FILE, - "process" - ), - // Model project consent given and broad consent given => process - Arguments.of( - buildMtb( - MvhMetadata.builder().modelProjectConsent( - ModelProjectConsent.builder().provisions( - listOf( - Provision.builder().date(Date()).type(ConsentProvision.PERMIT) - .purpose(ModelProjectConsentPurpose.SEQUENCING).build() + // Model project consent given and no broad consent => process + Arguments.of( + buildMtb( + MvhMetadata.builder() + .modelProjectConsent( + ModelProjectConsent.builder() + .provisions( + listOf( + Provision.builder() + .date(Date()) + .type(ConsentProvision.PERMIT) + .purpose(ModelProjectConsentPurpose.SEQUENCING) + .build() + ) + ) + .build() ) - ).build() - ).build() + .build() + ), + TtpConsentStatus.UNKNOWN_CHECK_FILE, + "process", ), - TtpConsentStatus.BROAD_CONSENT_GIVEN, - "process" - ) -) { - - companion object { - fun buildMtb(metadata: MvhMetadata?): Mtb { - return Mtb.builder() - .patient( - Patient.builder().id("TEST_12345678") - .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))).gender( - GenderCoding.builder().code(GenderCodingCode.MALE).build() - ).build() - ) - .metadata(metadata) - .episodesOfCare( - listOf( - MtbEpisodeOfCare.builder().id("1") - .patient(Reference.builder().id("TEST_12345678").build()) + // Model project consent given and broad consent given => process + Arguments.of( + buildMtb( + MvhMetadata.builder() + .modelProjectConsent( + ModelProjectConsent.builder() + .provisions( + listOf( + Provision.builder() + .date(Date()) + .type(ConsentProvision.PERMIT) + .purpose(ModelProjectConsentPurpose.SEQUENCING) + .build() + ) + ) .build() ) - ) - .build() - } + .build() + ), + TtpConsentStatus.BROAD_CONSENT_GIVEN, + "process", + ), + ) { + + companion object { + fun buildMtb(metadata: MvhMetadata?): Mtb { + return Mtb.builder() + .patient( + Patient.builder() + .id("TEST_12345678") + .birthDate(Date.from(Instant.parse("2000-08-08T12:34:56Z"))) + .gender(GenderCoding.builder().code(GenderCodingCode.MALE).build()) + .build() + ) + .metadata(metadata) + .episodesOfCare( + listOf( + MtbEpisodeOfCare.builder() + .id("1") + .patient(Reference.builder().id("TEST_12345678").build()) + .build() + ) + ) + .build() } + } } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt index a6d855c..a380d2a 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ConnectionCheckServiceTest.kt @@ -22,10 +22,8 @@ import reactor.test.StepVerifier @ExtendWith(MockitoExtension::class) class ConnectionCheckServiceTest { - @Nested inner class RestConnectionCheckServiceTest { - lateinit var mockRestServiceServer: MockRestServiceServer lateinit var service: RestConnectionCheckService lateinit var sink: Sinks.Many<ConnectionCheckResult> @@ -33,11 +31,12 @@ class ConnectionCheckServiceTest { @BeforeEach fun setUp() { val restTemplate = RestTemplate() - val restTargetProperties = RestTargetProperties( - "http://localhost/api", - "user", - "password", - ) + val restTargetProperties = + RestTargetProperties( + "http://localhost/api", + "user", + "password", + ) this.sink = Sinks.many().multicast().onBackpressureBuffer() this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) @@ -65,12 +64,12 @@ class ConnectionCheckServiceTest { withSuccess("OK", MediaType.APPLICATION_JSON), ) - val verifier = StepVerifier.create(sink.asFlux()) - .assertNext { - assertThat(it.available).isTrue() - } - .expectComplete() - .verifyLater() + val verifier = + StepVerifier + .create(sink.asFlux()) + .assertNext { assertThat(it.available).isTrue() } + .expectComplete() + .verifyLater() this.service.check() @@ -81,18 +80,14 @@ class ConnectionCheckServiceTest { @Test fun shouldEmitUnavailable() { - this.mockRestServiceServer - .expect(method(HttpMethod.GET)) - .andRespond( - withServerError() - ) + this.mockRestServiceServer.expect(method(HttpMethod.GET)).andRespond(withServerError()) - val verifier = StepVerifier.create(sink.asFlux()) - .assertNext { - assertThat(it.available).isFalse() - } - .expectComplete() - .verifyLater() + val verifier = + StepVerifier + .create(sink.asFlux()) + .assertNext { assertThat(it.available).isFalse() } + .expectComplete() + .verifyLater() this.service.check() @@ -104,7 +99,6 @@ class ConnectionCheckServiceTest { @Nested inner class GPasConnectionCheckServiceTest { - lateinit var mockRestServiceServer: MockRestServiceServer lateinit var service: GPasConnectionCheckService lateinit var sink: Sinks.Many<ConnectionCheckResult> @@ -112,15 +106,16 @@ class ConnectionCheckServiceTest { @BeforeEach fun setUp() { val restTemplate = RestTemplate() - val gpasTargetProperties = GPasConfigProperties( - "http://localhost/gpas", - null, - null, - "patientDomain", - "genomDeTanDomain", - "username", - "password", - ) + val gpasTargetProperties = + GPasConfigProperties( + "http://localhost/gpas", + null, + null, + "patientDomain", + "genomDeTanDomain", + "username", + "password", + ) this.sink = Sinks.many().multicast().onBackpressureBuffer() this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) @@ -149,12 +144,12 @@ class ConnectionCheckServiceTest { withSuccess("OK", MediaType.APPLICATION_JSON), ) - val verifier = StepVerifier.create(sink.asFlux()) - .assertNext { - assertThat(it.available).isTrue() - } - .expectComplete() - .verifyLater() + val verifier = + StepVerifier + .create(sink.asFlux()) + .assertNext { assertThat(it.available).isTrue() } + .expectComplete() + .verifyLater() this.service.check() @@ -165,18 +160,14 @@ class ConnectionCheckServiceTest { @Test fun shouldEmitUnavailable() { - this.mockRestServiceServer - .expect(method(HttpMethod.GET)) - .andRespond( - withServerError() - ) + this.mockRestServiceServer.expect(method(HttpMethod.GET)).andRespond(withServerError()) - val verifier = StepVerifier.create(sink.asFlux()) - .assertNext { - assertThat(it.available).isFalse() - } - .expectComplete() - .verifyLater() + val verifier = + StepVerifier + .create(sink.asFlux()) + .assertNext { assertThat(it.available).isFalse() } + .expectComplete() + .verifyLater() this.service.check() @@ -188,7 +179,6 @@ class ConnectionCheckServiceTest { @Nested inner class GIcsConnectionCheckServiceTest { - lateinit var mockRestServiceServer: MockRestServiceServer lateinit var service: GIcsConnectionCheckService lateinit var sink: Sinks.Many<ConnectionCheckResult> @@ -197,11 +187,12 @@ class ConnectionCheckServiceTest { fun setUp() { val restTemplate = RestTemplate() - val gicsTargetProperties = GIcsConfigProperties( - "http://localhost/gics", - "username", - "password", - ) + val gicsTargetProperties = + GIcsConfigProperties( + "http://localhost/gics", + "username", + "password", + ) this.sink = Sinks.many().multicast().onBackpressureBuffer() this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) @@ -220,7 +211,6 @@ class ConnectionCheckServiceTest { this.service.check() this.mockRestServiceServer.verify() - } @Test @@ -231,12 +221,12 @@ class ConnectionCheckServiceTest { withSuccess("OK", MediaType.APPLICATION_JSON), ) - val verifier = StepVerifier.create(sink.asFlux()) - .assertNext { - assertThat(it.available).isTrue() - } - .expectComplete() - .verifyLater() + val verifier = + StepVerifier + .create(sink.asFlux()) + .assertNext { assertThat(it.available).isTrue() } + .expectComplete() + .verifyLater() this.service.check() @@ -247,18 +237,14 @@ class ConnectionCheckServiceTest { @Test fun shouldEmitUnavailable() { - this.mockRestServiceServer - .expect(method(HttpMethod.GET)) - .andRespond( - withServerError() - ) + this.mockRestServiceServer.expect(method(HttpMethod.GET)).andRespond(withServerError()) - val verifier = StepVerifier.create(sink.asFlux()) - .assertNext { - assertThat(it.available).isFalse() - } - .expectComplete() - .verifyLater() + val verifier = + StepVerifier + .create(sink.asFlux()) + .assertNext { assertThat(it.available).isFalse() } + .expectComplete() + .verifyLater() this.service.check() @@ -267,5 +253,4 @@ class ConnectionCheckServiceTest { verifier.verify() } } - -}
\ No newline at end of file +} diff --git a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt index 4bf1321..74d1138 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/monitoring/ReportServiceTest.kt @@ -1,36 +1,36 @@ package dev.dnpm.etl.processor.monitoring import dev.dnpm.etl.processor.config.JacksonConfig +import java.util.* import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import java.util.* class ReportServiceTest { - lateinit var service: ReportService + lateinit var service: ReportService - @BeforeEach - fun setUp() { - val jacksonConfig = JacksonConfig() - service = ReportService(jacksonConfig.objectMapper()) - } + @BeforeEach + fun setUp() { + val jacksonConfig = JacksonConfig() + service = ReportService(jacksonConfig.objectMapper()) + } - @Test - fun shouldParseDataQualityReport() { - val dataQualityReport = Objects.requireNonNull(this.javaClass.classLoader.getResource("dip-response.json")) + @Test + fun shouldParseDataQualityReport() { + val dataQualityReport = + Objects.requireNonNull(this.javaClass.classLoader.getResource("dip-response.json")) .readText() - val actual = service.deserialize(dataQualityReport) - - assertThat(actual).isNotNull - assertThat(actual).hasSize(6) - assertThat(actual[0].severity).isEqualTo(ReportService.Severity.FATAL) - assertThat(actual[1].severity).isEqualTo(ReportService.Severity.ERROR) - assertThat(actual[2].severity).isEqualTo(ReportService.Severity.WARNING) - assertThat(actual[3].severity).isEqualTo(ReportService.Severity.WARNING) - assertThat(actual[4].severity).isEqualTo(ReportService.Severity.WARNING) - assertThat(actual[5].severity).isEqualTo(ReportService.Severity.INFO) - } - -}
\ No newline at end of file + val actual = service.deserialize(dataQualityReport) + + assertThat(actual).isNotNull + assertThat(actual).hasSize(6) + assertThat(actual[0].severity).isEqualTo(ReportService.Severity.FATAL) + assertThat(actual[1].severity).isEqualTo(ReportService.Severity.ERROR) + assertThat(actual[2].severity).isEqualTo(ReportService.Severity.WARNING) + assertThat(actual[3].severity).isEqualTo(ReportService.Severity.WARNING) + assertThat(actual[4].severity).isEqualTo(ReportService.Severity.WARNING) + assertThat(actual[5].severity).isEqualTo(ReportService.Severity.INFO) + } +} 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 022b8dd..b1cd5fa 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/output/KafkaMtbFileSenderTest.kt @@ -26,6 +26,10 @@ 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 java.time.Instant +import java.util.* +import java.util.concurrent.CompletableFuture.completedFuture +import java.util.concurrent.ExecutionException import org.apache.kafka.clients.producer.ProducerRecord import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach @@ -41,203 +45,208 @@ import org.springframework.kafka.core.KafkaTemplate import org.springframework.kafka.support.SendResult import org.springframework.retry.policy.SimpleRetryPolicy import org.springframework.retry.support.RetryTemplateBuilder -import java.time.Instant -import java.util.* -import java.util.concurrent.CompletableFuture.completedFuture -import java.util.concurrent.ExecutionException @ExtendWith(MockitoExtension::class) class KafkaMtbFileSenderTest { - @Nested - inner class BwhcV1Record { + @Nested + inner class BwhcV1Record { - private lateinit var kafkaTemplate: KafkaTemplate<String, String> + private lateinit var kafkaTemplate: KafkaTemplate<String, String> - private lateinit var kafkaMtbFileSender: KafkaMtbFileSender + private lateinit var kafkaMtbFileSender: KafkaMtbFileSender - private lateinit var objectMapper: ObjectMapper + private lateinit var objectMapper: ObjectMapper - @BeforeEach - fun setup( - @Mock kafkaTemplate: KafkaTemplate<String, String> - ) { - val kafkaProperties = KafkaProperties("testtopic") - val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() + @BeforeEach + fun setup(@Mock kafkaTemplate: KafkaTemplate<String, String>) { + val kafkaProperties = KafkaProperties("testtopic") + val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() - this.objectMapper = ObjectMapper() - this.kafkaTemplate = kafkaTemplate + this.objectMapper = ObjectMapper() + this.kafkaTemplate = kafkaTemplate - this.kafkaMtbFileSender = KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper) - } - - @ParameterizedTest - @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource") - fun shouldSendDeleteRequestAndReturnExpectedState(testData: TestData) { - doAnswer { - if (null != testData.exception) { - throw testData.exception - } - completedFuture(SendResult<String, String>(null, null)) - }.whenever(kafkaTemplate).send(any<ProducerRecord<String, String>>()) - - val response = kafkaMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) - assertThat(response.status).isEqualTo(testData.requestStatus) - } - - @ParameterizedTest - @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource") - fun shouldRetryOnDeleteKafkaSendError(testData: TestData) { - val kafkaProperties = KafkaProperties("testtopic") - val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build() - this.kafkaMtbFileSender = KafkaMtbFileSender(this.kafkaTemplate, kafkaProperties, retryTemplate, this.objectMapper) - - doAnswer { - if (null != testData.exception) { - throw testData.exception - } - completedFuture(SendResult<String, String>(null, null)) - }.whenever(kafkaTemplate).send(any<ProducerRecord<String, String>>()) - - kafkaMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) + this.kafkaMtbFileSender = + KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper) + } - val expectedCount = when (testData.exception) { - // OK - No Retry - null -> times(1) - // Request failed - Retry max 3 times - else -> times(3) + @ParameterizedTest + @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource") + fun shouldSendDeleteRequestAndReturnExpectedState(testData: TestData) { + doAnswer { + if (null != testData.exception) { + throw testData.exception } + completedFuture(SendResult<String, String>(null, null)) + } + .whenever(kafkaTemplate) + .send(any<ProducerRecord<String, String>>()) - verify(kafkaTemplate, expectedCount).send(any<ProducerRecord<String, String>>()) - } - + val response = kafkaMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) + assertThat(response.status).isEqualTo(testData.requestStatus) } - @Nested - inner class DnpmV2Record { - - private lateinit var kafkaTemplate: KafkaTemplate<String, String> + @ParameterizedTest + @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource") + fun shouldRetryOnDeleteKafkaSendError(testData: TestData) { + val kafkaProperties = KafkaProperties("testtopic") + val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build() + this.kafkaMtbFileSender = + KafkaMtbFileSender(this.kafkaTemplate, kafkaProperties, retryTemplate, this.objectMapper) + + doAnswer { + if (null != testData.exception) { + throw testData.exception + } + completedFuture(SendResult<String, String>(null, null)) + } + .whenever(kafkaTemplate) + .send(any<ProducerRecord<String, String>>()) + + kafkaMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) + + val expectedCount = + when (testData.exception) { + // OK - No Retry + null -> times(1) + // Request failed - Retry max 3 times + else -> times(3) + } + + verify(kafkaTemplate, expectedCount).send(any<ProducerRecord<String, String>>()) + } + } - private lateinit var kafkaMtbFileSender: KafkaMtbFileSender + @Nested + inner class DnpmV2Record { - private lateinit var objectMapper: ObjectMapper + private lateinit var kafkaTemplate: KafkaTemplate<String, String> - @BeforeEach - fun setup( - @Mock kafkaTemplate: KafkaTemplate<String, String> - ) { - val kafkaProperties = KafkaProperties("testtopic") - val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() + private lateinit var kafkaMtbFileSender: KafkaMtbFileSender - this.objectMapper = ObjectMapper() - this.kafkaTemplate = kafkaTemplate + private lateinit var objectMapper: ObjectMapper - this.kafkaMtbFileSender = KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper) - } + @BeforeEach + fun setup(@Mock kafkaTemplate: KafkaTemplate<String, String>) { + val kafkaProperties = KafkaProperties("testtopic") + val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() - @ParameterizedTest - @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource") - fun shouldSendMtbFileRequestAndReturnExpectedState(testData: TestData) { - doAnswer { - if (null != testData.exception) { - throw testData.exception - } - completedFuture(SendResult<String, String>(null, null)) - }.whenever(kafkaTemplate).send(any<ProducerRecord<String, String>>()) - - val response = kafkaMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile())) - assertThat(response.status).isEqualTo(testData.requestStatus) - } - - @Test - fun shouldSendMtbFileRequestWithCorrectKeyAndHeaderAndBody() { - doAnswer { - completedFuture(SendResult<String, String>(null, null)) - }.whenever(kafkaTemplate).send(any<ProducerRecord<String, String>>()) - - kafkaMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile())) - - val captor = argumentCaptor<ProducerRecord<String, String>>() - verify(kafkaTemplate, times(1)).send(captor.capture()) - assertThat(captor.firstValue.key()).isNotNull - assertThat(captor.firstValue.key()).isEqualTo("{\"pid\": \"PID\"}") - assertThat(captor.firstValue.headers().headers("contentType")).isNotNull - assertThat(captor.firstValue.headers().headers("contentType")?.firstOrNull()?.value()).isEqualTo(CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE.toByteArray()) - assertThat(captor.firstValue.headers().headers("requestId")).isNotNull - assertThat(captor.firstValue.headers().headers("requestId")?.firstOrNull()?.value()).isEqualTo(TEST_REQUEST_ID.value.toByteArray()) - assertThat(captor.firstValue.value()).isNotNull - assertThat(captor.firstValue.value()).isEqualTo(objectMapper.writeValueAsString(dnmpV2kafkaRecordData(TEST_REQUEST_ID))) - } - - @ParameterizedTest - @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource") - fun shouldRetryOnMtbFileKafkaSendError(testData: TestData) { - val kafkaProperties = KafkaProperties("testtopic") - val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build() - this.kafkaMtbFileSender = KafkaMtbFileSender(this.kafkaTemplate, kafkaProperties, retryTemplate, this.objectMapper) - - doAnswer { - if (null != testData.exception) { - throw testData.exception - } - completedFuture(SendResult<String, String>(null, null)) - }.whenever(kafkaTemplate).send(any<ProducerRecord<String, String>>()) + this.objectMapper = ObjectMapper() + this.kafkaTemplate = kafkaTemplate - kafkaMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile())) + this.kafkaMtbFileSender = + KafkaMtbFileSender(kafkaTemplate, kafkaProperties, retryTemplate, objectMapper) + } - val expectedCount = when (testData.exception) { - // OK - No Retry - null -> times(1) - // Request failed - Retry max 3 times - else -> times(3) + @ParameterizedTest + @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource") + fun shouldSendMtbFileRequestAndReturnExpectedState(testData: TestData) { + doAnswer { + if (null != testData.exception) { + throw testData.exception } + completedFuture(SendResult<String, String>(null, null)) + } + .whenever(kafkaTemplate) + .send(any<ProducerRecord<String, String>>()) - verify(kafkaTemplate, expectedCount).send(any<ProducerRecord<String, String>>()) - } + val response = kafkaMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile())) + assertThat(response.status).isEqualTo(testData.requestStatus) + } + @Test + fun shouldSendMtbFileRequestWithCorrectKeyAndHeaderAndBody() { + doAnswer { completedFuture(SendResult<String, String>(null, null)) } + .whenever(kafkaTemplate) + .send(any<ProducerRecord<String, String>>()) + + kafkaMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile())) + + val captor = argumentCaptor<ProducerRecord<String, String>>() + verify(kafkaTemplate, times(1)).send(captor.capture()) + assertThat(captor.firstValue.key()).isNotNull + assertThat(captor.firstValue.key()).isEqualTo("{\"pid\": \"PID\"}") + assertThat(captor.firstValue.headers().headers("contentType")).isNotNull + assertThat(captor.firstValue.headers().headers("contentType")?.firstOrNull()?.value()) + .isEqualTo(CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE.toByteArray()) + assertThat(captor.firstValue.headers().headers("requestId")).isNotNull + assertThat(captor.firstValue.headers().headers("requestId")?.firstOrNull()?.value()) + .isEqualTo(TEST_REQUEST_ID.value.toByteArray()) + assertThat(captor.firstValue.value()).isNotNull + assertThat(captor.firstValue.value()) + .isEqualTo(objectMapper.writeValueAsString(dnmpV2kafkaRecordData(TEST_REQUEST_ID))) } - companion object { - val TEST_REQUEST_ID = RequestId("TestId") - val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID") - - fun dnpmV2MtbFile(): Mtb { - return Mtb().apply { - this.patient = dev.pcvolkmer.mv64e.mtb.Patient().apply { - this.id = "PID" - this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { - this.code = GenderCodingCode.MALE - } - } - this.episodesOfCare = listOf( - MtbEpisodeOfCare().apply { - this.id = "1" - this.patient = Reference().apply { - this.id = "PID" - } - this.period = PeriodDate().apply { - this.start = Date.from(Instant.now()) - } - } - ) + @ParameterizedTest + @MethodSource("dev.dnpm.etl.processor.output.KafkaMtbFileSenderTest#requestWithResponseSource") + fun shouldRetryOnMtbFileKafkaSendError(testData: TestData) { + val kafkaProperties = KafkaProperties("testtopic") + val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(3)).build() + this.kafkaMtbFileSender = + KafkaMtbFileSender(this.kafkaTemplate, kafkaProperties, retryTemplate, this.objectMapper) + + doAnswer { + if (null != testData.exception) { + throw testData.exception } - } - - fun dnmpV2kafkaRecordData(requestId: RequestId): Mtb { - return DnpmV2MtbFileRequest(requestId, dnpmV2MtbFile()).content - } - - data class TestData(val requestStatus: RequestStatus, val exception: Throwable? = null) - - @JvmStatic - fun requestWithResponseSource(): Set<TestData> { - return setOf( - TestData(RequestStatus.UNKNOWN), - TestData(RequestStatus.ERROR, InterruptedException("Test interrupted")), - TestData(RequestStatus.ERROR, ExecutionException(RuntimeException("Test execution aborted"))) + completedFuture(SendResult<String, String>(null, null)) + } + .whenever(kafkaTemplate) + .send(any<ProducerRecord<String, String>>()) + + kafkaMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile())) + + val expectedCount = + when (testData.exception) { + // OK - No Retry + null -> times(1) + // Request failed - Retry max 3 times + else -> times(3) + } + + verify(kafkaTemplate, expectedCount).send(any<ProducerRecord<String, String>>()) + } + } + + companion object { + val TEST_REQUEST_ID = RequestId("TestId") + val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID") + + fun dnpmV2MtbFile(): Mtb { + return Mtb().apply { + this.patient = + dev.pcvolkmer.mv64e.mtb.Patient().apply { + this.id = "PID" + this.birthDate = Date.from(Instant.now()) + this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + } + this.episodesOfCare = + listOf( + MtbEpisodeOfCare().apply { + this.id = "1" + this.patient = Reference().apply { this.id = "PID" } + this.period = PeriodDate().apply { this.start = Date.from(Instant.now()) } + } ) - } + } } + fun dnmpV2kafkaRecordData(requestId: RequestId): Mtb { + return DnpmV2MtbFileRequest(requestId, dnpmV2MtbFile()).content + } + + data class TestData(val requestStatus: RequestStatus, val exception: Throwable? = null) + + @JvmStatic + fun requestWithResponseSource(): Set<TestData> { + return setOf( + TestData(RequestStatus.UNKNOWN), + TestData(RequestStatus.ERROR, InterruptedException("Test interrupted")), + TestData( + RequestStatus.ERROR, + ExecutionException(RuntimeException("Test execution aborted")), + ), + ) + } + } } 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 1b27a62..2ab0218 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/output/RestDipMtbFileSenderTest.kt @@ -30,6 +30,8 @@ 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 java.time.Instant +import java.util.* import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested @@ -38,7 +40,6 @@ import org.junit.jupiter.params.provider.MethodSource import org.springframework.http.HttpHeaders import org.springframework.http.HttpMethod import org.springframework.http.HttpStatus -import org.springframework.http.MediaType import org.springframework.retry.backoff.NoBackOffPolicy import org.springframework.retry.policy.SimpleRetryPolicy import org.springframework.retry.support.RetryTemplateBuilder @@ -47,224 +48,252 @@ import org.springframework.test.web.client.MockRestServiceServer import org.springframework.test.web.client.match.MockRestRequestMatchers.* import org.springframework.test.web.client.response.MockRestResponseCreators.withStatus import org.springframework.web.client.RestTemplate -import java.time.Instant -import java.util.* class RestDipMtbFileSenderTest { - @Nested - inner class DnpmV2ContentRequest { + @Nested + inner class DnpmV2ContentRequest { - private lateinit var mockRestServiceServer: MockRestServiceServer + private lateinit var mockRestServiceServer: MockRestServiceServer - private lateinit var restMtbFileSender: RestMtbFileSender + private lateinit var restMtbFileSender: RestMtbFileSender - private var reportService = ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build())) + private var reportService = + ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build())) - @BeforeEach - fun setup() { - val restTemplate = RestTemplate() - val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) - val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() + @BeforeEach + fun setup() { + val restTemplate = RestTemplate() + val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) + val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() - this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) - - this.restMtbFileSender = RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) - } - - @ParameterizedTest - @MethodSource("dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#mtbFileRequestWithResponseSource") - fun shouldReturnExpectedResponseForDnpmV2MtbFilePost(requestWithResponse: RequestWithResponse) { - this.mockRestServiceServer - .expect(method(HttpMethod.POST)) - .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient-record")) - .andExpect(header(HttpHeaders.CONTENT_TYPE, CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE)) - .andRespond { - withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it) - } + this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) - val response = restMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile())) - assertThat(response.status).isEqualTo(requestWithResponse.response.status) - assertThat(response.body).isEqualTo(requestWithResponse.response.body) - } + this.restMtbFileSender = + RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) + } + @ParameterizedTest + @MethodSource( + "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#mtbFileRequestWithResponseSource" + ) + fun shouldReturnExpectedResponseForDnpmV2MtbFilePost(requestWithResponse: RequestWithResponse) { + this.mockRestServiceServer + .expect(method(HttpMethod.POST)) + .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient-record")) + .andExpect( + header( + HttpHeaders.CONTENT_TYPE, + CustomMediaType.APPLICATION_VND_DNPM_V2_MTB_JSON_VALUE, + ) + ) + .andRespond { + withStatus(requestWithResponse.httpStatus) + .body(requestWithResponse.body) + .createResponse(it) + } + + val response = restMtbFileSender.send(DnpmV2MtbFileRequest(TEST_REQUEST_ID, dnpmV2MtbFile())) + assertThat(response.status).isEqualTo(requestWithResponse.response.status) + assertThat(response.body).isEqualTo(requestWithResponse.response.body) } + } - @Nested - inner class DeleteRequest { + @Nested + inner class DeleteRequest { - private lateinit var mockRestServiceServer: MockRestServiceServer + private lateinit var mockRestServiceServer: MockRestServiceServer - private lateinit var restMtbFileSender: RestMtbFileSender + private lateinit var restMtbFileSender: RestMtbFileSender - private var reportService = ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build())) + private var reportService = + ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build())) - @BeforeEach - fun setup() { - val restTemplate = RestTemplate() - val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) - val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() + @BeforeEach + fun setup() { + val restTemplate = RestTemplate() + val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) + val retryTemplate = RetryTemplateBuilder().customPolicy(SimpleRetryPolicy(1)).build() - this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) + this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) - this.restMtbFileSender = - RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) - } + this.restMtbFileSender = + RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) + } - @ParameterizedTest - @MethodSource("dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource") - fun shouldReturnExpectedResponseForDelete(requestWithResponse: RequestWithResponse) { - this.mockRestServiceServer - .expect(method(HttpMethod.DELETE)) - .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient/${TEST_PATIENT_PSEUDONYM.value}")) - .andRespond { - withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it) - } + @ParameterizedTest + @MethodSource( + "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource" + ) + fun shouldReturnExpectedResponseForDelete(requestWithResponse: RequestWithResponse) { + this.mockRestServiceServer + .expect(method(HttpMethod.DELETE)) + .andExpect( + requestTo("http://localhost:9000/api/mtb/etl/patient/${TEST_PATIENT_PSEUDONYM.value}") + ) + .andRespond { + withStatus(requestWithResponse.httpStatus) + .body(requestWithResponse.body) + .createResponse(it) + } + + val response = restMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) + assertThat(response.status).isEqualTo(requestWithResponse.response.status) + assertThat(response.body).isEqualTo(requestWithResponse.response.body) + } - val response = restMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) - assertThat(response.status).isEqualTo(requestWithResponse.response.status) - assertThat(response.body).isEqualTo(requestWithResponse.response.body) - } - - @ParameterizedTest - @MethodSource("dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource") - fun shouldRetryOnDeleteHttpRequestError(requestWithResponse: RequestWithResponse) { - val restTemplate = RestTemplate() - val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) - val retryTemplate = AppConfiguration().retryTemplate(AppConfigProperties()) - retryTemplate.setBackOffPolicy(NoBackOffPolicy()) - - this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) - this.restMtbFileSender = - RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) - - val expectedCount = when (requestWithResponse.httpStatus) { - // OK - No Retry - HttpStatus.OK, HttpStatus.CREATED, HttpStatus.UNPROCESSABLE_ENTITY, HttpStatus.BAD_REQUEST -> ExpectedCount.max( - 1 - ) - // Request failed - Retry max 3 times - else -> ExpectedCount.max(3) + @ParameterizedTest + @MethodSource( + "dev.dnpm.etl.processor.output.RestDipMtbFileSenderTest#deleteRequestWithResponseSource" + ) + fun shouldRetryOnDeleteHttpRequestError(requestWithResponse: RequestWithResponse) { + val restTemplate = RestTemplate() + val restTargetProperties = RestTargetProperties("http://localhost:9000/api", null, null) + val retryTemplate = AppConfiguration().retryTemplate(AppConfigProperties()) + retryTemplate.setBackOffPolicy(NoBackOffPolicy()) + + this.mockRestServiceServer = MockRestServiceServer.createServer(restTemplate) + this.restMtbFileSender = + RestDipMtbFileSender(restTemplate, restTargetProperties, retryTemplate, reportService) + + val expectedCount = + when (requestWithResponse.httpStatus) { + // OK - No Retry + HttpStatus.OK, + HttpStatus.CREATED, + HttpStatus.UNPROCESSABLE_ENTITY, + HttpStatus.BAD_REQUEST -> ExpectedCount.max(1) + // Request failed - Retry max 3 times + else -> ExpectedCount.max(3) + } + + this.mockRestServiceServer + .expect(expectedCount, method(HttpMethod.DELETE)) + .andExpect( + requestTo("http://localhost:9000/api/mtb/etl/patient/${TEST_PATIENT_PSEUDONYM.value}") + ) + .andRespond { + withStatus(requestWithResponse.httpStatus) + .body(requestWithResponse.body) + .createResponse(it) + } + + val response = restMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) + assertThat(response.status).isEqualTo(requestWithResponse.response.status) + assertThat(response.body).isEqualTo(requestWithResponse.response.body) + } + } + + companion object { + data class RequestWithResponse( + val httpStatus: HttpStatus, + val body: String, + val response: MtbFileSender.Response, + ) + + val TEST_REQUEST_ID = RequestId("TestId") + val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID") + + fun dnpmV2MtbFile(): Mtb { + return Mtb().apply { + this.patient = + dev.pcvolkmer.mv64e.mtb.Patient().apply { + this.id = "PID" + this.birthDate = Date.from(Instant.now()) + this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } } - - this.mockRestServiceServer - .expect(expectedCount, method(HttpMethod.DELETE)) - .andExpect(requestTo("http://localhost:9000/api/mtb/etl/patient/${TEST_PATIENT_PSEUDONYM.value}")) - .andRespond { - withStatus(requestWithResponse.httpStatus).body(requestWithResponse.body).createResponse(it) + this.episodesOfCare = + listOf( + MtbEpisodeOfCare().apply { + this.id = "1" + this.patient = Reference().apply { this.id = "PID" } + this.period = PeriodDate().apply { this.start = Date.from(Instant.now()) } } + ) + } + } - val response = restMtbFileSender.send(DeleteRequest(TEST_REQUEST_ID, TEST_PATIENT_PSEUDONYM)) - assertThat(response.status).isEqualTo(requestWithResponse.response.status) - assertThat(response.body).isEqualTo(requestWithResponse.response.body) - } - + private const val ERROR_RESPONSE_BODY = "Sonstiger Fehler bei der Übertragung" + + /** + * Synthetic http responses with related request status Also see: + * https://ibmi-intra.cs.uni-tuebingen.de/display/ZPM/bwHC+REST+API + */ + @JvmStatic + fun mtbFileRequestWithResponseSource(): Set<RequestWithResponse> { + return setOf( + RequestWithResponse( + HttpStatus.OK, + responseBodyWithMaxSeverity(ReportService.Severity.INFO), + MtbFileSender.Response( + RequestStatus.SUCCESS, + responseBodyWithMaxSeverity(ReportService.Severity.INFO), + ), + ), + RequestWithResponse( + HttpStatus.CREATED, + responseBodyWithMaxSeverity(ReportService.Severity.WARNING), + MtbFileSender.Response( + RequestStatus.WARNING, + responseBodyWithMaxSeverity(ReportService.Severity.WARNING), + ), + ), + RequestWithResponse( + HttpStatus.BAD_REQUEST, + responseBodyWithMaxSeverity(ReportService.Severity.ERROR), + MtbFileSender.Response( + RequestStatus.ERROR, + responseBodyWithMaxSeverity(ReportService.Severity.ERROR), + ), + ), + RequestWithResponse( + HttpStatus.UNPROCESSABLE_ENTITY, + responseBodyWithMaxSeverity(ReportService.Severity.ERROR), + MtbFileSender.Response( + RequestStatus.ERROR, + responseBodyWithMaxSeverity(ReportService.Severity.ERROR), + ), + ), + // Some more errors not mentioned in documentation + RequestWithResponse( + HttpStatus.NOT_FOUND, + ERROR_RESPONSE_BODY, + MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), + ), + RequestWithResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + ERROR_RESPONSE_BODY, + MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), + ), + ) } - companion object { - data class RequestWithResponse( - val httpStatus: HttpStatus, - val body: String, - val response: MtbFileSender.Response - ) - - val TEST_REQUEST_ID = RequestId("TestId") - val TEST_PATIENT_PSEUDONYM = PatientPseudonym("PID") - - fun dnpmV2MtbFile(): Mtb { - return Mtb().apply { - this.patient = dev.pcvolkmer.mv64e.mtb.Patient().apply { - this.id = "PID" - this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { - this.code = GenderCodingCode.MALE - } - } - this.episodesOfCare = listOf( - MtbEpisodeOfCare().apply { - this.id = "1" - this.patient = Reference().apply { - this.id = "PID" - } - this.period = PeriodDate().apply { - this.start = Date.from(Instant.now()) - } - } - ) - } - } - - private const val ERROR_RESPONSE_BODY = "Sonstiger Fehler bei der Übertragung" - - /** - * Synthetic http responses with related request status - * Also see: https://ibmi-intra.cs.uni-tuebingen.de/display/ZPM/bwHC+REST+API - */ - @JvmStatic - fun mtbFileRequestWithResponseSource(): Set<RequestWithResponse> { - return setOf( - RequestWithResponse( - HttpStatus.OK, - responseBodyWithMaxSeverity(ReportService.Severity.INFO), - MtbFileSender.Response( - RequestStatus.SUCCESS, - responseBodyWithMaxSeverity(ReportService.Severity.INFO) - ) - ), - RequestWithResponse( - HttpStatus.CREATED, - responseBodyWithMaxSeverity(ReportService.Severity.WARNING), - MtbFileSender.Response(RequestStatus.WARNING, responseBodyWithMaxSeverity(ReportService.Severity.WARNING)) - ), - RequestWithResponse( - HttpStatus.BAD_REQUEST, - responseBodyWithMaxSeverity(ReportService.Severity.ERROR), - MtbFileSender.Response(RequestStatus.ERROR, responseBodyWithMaxSeverity(ReportService.Severity.ERROR)) - ), - RequestWithResponse( - HttpStatus.UNPROCESSABLE_ENTITY, - responseBodyWithMaxSeverity(ReportService.Severity.ERROR), - MtbFileSender.Response(RequestStatus.ERROR, responseBodyWithMaxSeverity(ReportService.Severity.ERROR)) - ), - // Some more errors not mentioned in documentation - RequestWithResponse( - HttpStatus.NOT_FOUND, - ERROR_RESPONSE_BODY, - MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY) - ), - RequestWithResponse( - HttpStatus.INTERNAL_SERVER_ERROR, - ERROR_RESPONSE_BODY, - MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY) - ) - ) - } - - /** - * Synthetic http responses with related request status - * Also see: https://ibmi-intra.cs.uni-tuebingen.de/display/ZPM/bwHC+REST+API - */ - @JvmStatic - fun deleteRequestWithResponseSource(): Set<RequestWithResponse> { - return setOf( - RequestWithResponse(HttpStatus.OK, "", MtbFileSender.Response(RequestStatus.SUCCESS)), - // Some more errors not mentioned in documentation - RequestWithResponse( - HttpStatus.NOT_FOUND, - "what????", - MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY) - ), - RequestWithResponse( - HttpStatus.INTERNAL_SERVER_ERROR, - "what????", - MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY) - ) - ) - } + /** + * Synthetic http responses with related request status Also see: + * https://ibmi-intra.cs.uni-tuebingen.de/display/ZPM/bwHC+REST+API + */ + @JvmStatic + fun deleteRequestWithResponseSource(): Set<RequestWithResponse> { + return setOf( + RequestWithResponse(HttpStatus.OK, "", MtbFileSender.Response(RequestStatus.SUCCESS)), + // Some more errors not mentioned in documentation + RequestWithResponse( + HttpStatus.NOT_FOUND, + "what????", + MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), + ), + RequestWithResponse( + HttpStatus.INTERNAL_SERVER_ERROR, + "what????", + MtbFileSender.Response(RequestStatus.ERROR, ERROR_RESPONSE_BODY), + ), + ) + } - fun responseBodyWithMaxSeverity(severity: ReportService.Severity): String { - return when (severity) { - ReportService.Severity.INFO -> """ + fun responseBodyWithMaxSeverity(severity: ReportService.Severity): String { + return when (severity) { + ReportService.Severity.INFO -> + """ { "patient": "PID", "issues": [ @@ -273,7 +302,8 @@ class RestDipMtbFileSenderTest { } """ - ReportService.Severity.WARNING -> """ + ReportService.Severity.WARNING -> + """ { "patient": "PID", "issues": [ @@ -283,7 +313,8 @@ class RestDipMtbFileSenderTest { } """ - ReportService.Severity.ERROR -> """ + ReportService.Severity.ERROR -> + """ { "patient": "PID", "issues": [ @@ -294,7 +325,8 @@ class RestDipMtbFileSenderTest { } """ - ReportService.Severity.FATAL -> """ + ReportService.Severity.FATAL -> + """ { "patient": "PID", "issues": [ @@ -305,9 +337,7 @@ class RestDipMtbFileSenderTest { ] } """ - } - } + } } - - + } } 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 c302362..a0b3b24 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/ExtensionsTest.kt @@ -28,6 +28,8 @@ 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 java.time.Instant +import java.util.* import org.assertj.core.api.Assertions.assertThat import org.hl7.fhir.r4.model.Bundle import org.junit.jupiter.api.Nested @@ -40,231 +42,212 @@ import org.mockito.kotlin.anyValueClass import org.mockito.kotlin.doAnswer import org.mockito.kotlin.whenever import org.springframework.core.io.ClassPathResource -import java.time.Instant -import java.util.* @ExtendWith(MockitoExtension::class) class ExtensionsTest { - fun getObjectMapper(): ObjectMapper { - return JacksonConfig().objectMapper() - } - - @Nested - inner class UsingDnpmV2Datamodel { + fun getObjectMapper(): ObjectMapper { + return JacksonConfig().objectMapper() + } - val FAKE_MTB_FILE_PATH = "mv64e-mtb-fake-patient.json" - val CLEAN_PATIENT_ID = "644bae7a-56f6-4ee8-b02f-c532e65af5b1" + @Nested + inner class UsingDnpmV2Datamodel { - private fun fakeMtbFile(): Mtb { - val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream - return getObjectMapper().readValue(mtbFile, Mtb::class.java) - } + val FAKE_MTB_FILE_PATH = "mv64e-mtb-fake-patient.json" + val CLEAN_PATIENT_ID = "644bae7a-56f6-4ee8-b02f-c532e65af5b1" - private fun Mtb.serialized(): String { - return getObjectMapper().writeValueAsString(this) - } - - @Test - fun shouldNotContainCleanPatientId(@Mock pseudonymizeService: PseudonymizeService) { - doAnswer { - it.arguments[0] - "PSEUDO-ID" - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - val mtbFile = fakeMtbFile() - mtbFile.ensureMetaDataIsInitialized() - addConsentData(mtbFile) + private fun fakeMtbFile(): Mtb { + val mtbFile = ClassPathResource(FAKE_MTB_FILE_PATH).inputStream + return getObjectMapper().readValue(mtbFile, Mtb::class.java) + } - mtbFile.pseudonymizeWith(pseudonymizeService) + private fun Mtb.serialized(): String { + return getObjectMapper().writeValueAsString(this) + } - assertThat(mtbFile.patient.id).isEqualTo("PSEUDO-ID") - assertThat(mtbFile.serialized()).doesNotContain(CLEAN_PATIENT_ID) - } + @Test + fun shouldNotContainCleanPatientId(@Mock pseudonymizeService: PseudonymizeService) { + doAnswer { + it.arguments[0] + "PSEUDO-ID" + } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) - private fun addConsentData(mtbFile: Mtb) { - val gIcsConfigProperties = GIcsConfigProperties("", "", "") - val appConfigProperties = AppConfigProperties(emptyList()) + val mtbFile = fakeMtbFile() + mtbFile.ensureMetaDataIsInitialized() + addConsentData(mtbFile) - val bundle = Bundle() - val dummyConsent = ConsentProcessorTest.getDummyGenomDeConsent() - dummyConsent.patient.reference = "Patient/$CLEAN_PATIENT_ID" - bundle.addEntry().resource = dummyConsent + mtbFile.pseudonymizeWith(pseudonymizeService) - ConsentProcessor( - appConfigProperties, - gIcsConfigProperties, - JacksonConfig().objectMapper(), - FhirContext.forR4(), - MtbFileConsentService() - ).embedBroadConsentResources(mtbFile, bundle) + assertThat(mtbFile.patient.id).isEqualTo("PSEUDO-ID") + assertThat(mtbFile.serialized()).doesNotContain(CLEAN_PATIENT_ID) + } - } + private fun addConsentData(mtbFile: Mtb) { + val gIcsConfigProperties = GIcsConfigProperties("", "", "") + val appConfigProperties = AppConfigProperties(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(), + MtbFileConsentService(), + ) + .embedBroadConsentResources(mtbFile, bundle) + } - @Test - fun shouldNotThrowExceptionOnNullValues(@Mock pseudonymizeService: PseudonymizeService) { - doAnswer { - it.arguments[0] - "PSEUDO-ID" - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - doAnswer { - "TESTDOMAIN" - }.whenever(pseudonymizeService).prefix() - - val mtbFile = Mtb().apply { - this.patient = Patient().apply { - this.id = "PID" - this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { - this.code = GenderCodingCode.MALE - } + @Test + fun shouldNotThrowExceptionOnNullValues(@Mock pseudonymizeService: PseudonymizeService) { + doAnswer { + it.arguments[0] + "PSEUDO-ID" + } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() + + val mtbFile = + Mtb().apply { + this.patient = + Patient().apply { + this.id = "PID" + this.birthDate = Date.from(Instant.now()) + this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } } - this.episodesOfCare = listOf( + this.episodesOfCare = + listOf( MtbEpisodeOfCare().apply { - this.id = "1" - this.patient = Reference().apply { - this.id = "PID" - } - this.period = PeriodDate().apply { - this.start = Date.from(Instant.now()) - } + this.id = "1" + this.patient = Reference().apply { this.id = "PID" } + this.period = PeriodDate().apply { this.start = Date.from(Instant.now()) } } ) - } + } - mtbFile.pseudonymizeWith(pseudonymizeService) - mtbFile.anonymizeContentWith(pseudonymizeService) + mtbFile.pseudonymizeWith(pseudonymizeService) + mtbFile.anonymizeContentWith(pseudonymizeService) - assertThat(mtbFile.episodesOfCare).hasSize(1) - assertThat(mtbFile.episodesOfCare.map { it.id }).isNotNull - } - - @Test - fun shouldNotContainAnyUuidAfterRehashingOfIds(@Mock pseudonymizeService: PseudonymizeService) { - doAnswer { - it.arguments[0] - "PSEUDO-ID" - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - doAnswer { - "TESTDOMAIN" - }.whenever(pseudonymizeService).prefix() - - 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 -> - extIdValue.value = - Math.random().toLong().toString() - } - } - } - 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.externalIds?.forEach { - it?.value = Math.random().toLong().toString() - } - } - } - - 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 input = mtbFile.serialized() - val matcher = pattern.matcher(input) - - assertThrows<IllegalStateException> { - matcher.find() - val posSt = "check at pos: " + matcher.start().toString() + ", " + matcher.end() - println(posSt + " with " + matcher.group()) - }.also { - assertThat(it.message).isEqualTo("No match found") - } - } + assertThat(mtbFile.episodesOfCare).hasSize(1) + assertThat(mtbFile.episodesOfCare.map { it.id }).isNotNull } @Test - fun shouldUseSameAnonymIdForDiagnosisAndDiagnosisReferences(@Mock pseudonymizeService: PseudonymizeService) { - - doAnswer { + fun shouldNotContainAnyUuidAfterRehashingOfIds(@Mock pseudonymizeService: PseudonymizeService) { + doAnswer { it.arguments[0] "PSEUDO-ID" - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) + } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() - doAnswer { - "TESTDOMAIN" - }.whenever(pseudonymizeService).prefix() + val mtbFile = fakeMtbFile() - val mtbFile = Mtb().apply { - this.patient = Patient().apply { + /** 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 -> + extIdValue.value = Math.random().toLong().toString() + } + } + } + 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.externalIds?.forEach { it?.value = Math.random().toLong().toString() } + } + } + + 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 input = mtbFile.serialized() + val matcher = pattern.matcher(input) + + assertThrows<IllegalStateException> { + matcher.find() + val posSt = "check at pos: " + matcher.start().toString() + ", " + matcher.end() + println(posSt + " with " + matcher.group()) + } + .also { assertThat(it.message).isEqualTo("No match found") } + } + } + + @Test + fun shouldUseSameAnonymIdForDiagnosisAndDiagnosisReferences( + @Mock pseudonymizeService: PseudonymizeService + ) { + + doAnswer { + it.arguments[0] + "PSEUDO-ID" + } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { "TESTDOMAIN" }.whenever(pseudonymizeService).prefix() + + val mtbFile = + Mtb().apply { + this.patient = + Patient().apply { this.id = "PID" this.birthDate = Date.from(Instant.now()) - this.gender = GenderCoding().apply { - this.code = GenderCodingCode.MALE - } - } - this.diagnoses = listOf( - MtbDiagnosis().apply { - this.id = "Diagnosis-1" - } - ) - this.episodesOfCare = listOf( - MtbEpisodeOfCare().apply { + this.gender = GenderCoding().apply { this.code = GenderCodingCode.MALE } + } + this.diagnoses = listOf(MtbDiagnosis().apply { this.id = "Diagnosis-1" }) + this.episodesOfCare = + listOf( + MtbEpisodeOfCare().apply { this.id = "Episode-1" - this.diagnoses = listOf( - Reference().apply { - this.id = "Diagnosis-1" - } - ) - } - ) - this.guidelineTherapies = listOf( - MtbSystemicTherapy().apply { + this.diagnoses = listOf(Reference().apply { this.id = "Diagnosis-1" }) + } + ) + this.guidelineTherapies = + listOf( + MtbSystemicTherapy().apply { this.id = "Systemic-Therapy-1" - this.reason = Reference().apply { - this.id = "Diagnosis-1" - } - } - ) - this.guidelineProcedures = listOf( - OncoProcedure().apply { + this.reason = Reference().apply { this.id = "Diagnosis-1" } + } + ) + this.guidelineProcedures = + listOf( + OncoProcedure().apply { this.id = "Onco-Procedure-1" - this.reason = Reference().apply { - this.id = "Diagnosis-1" - } - } - ) - this.specimens = listOf( - TumorSpecimen().apply { + this.reason = Reference().apply { this.id = "Diagnosis-1" } + } + ) + this.specimens = + listOf( + TumorSpecimen().apply { this.id = "Specimen-1" - this.diagnosis = Reference().apply { - this.id = "Diagnosis-1" - } - } - ) + this.diagnosis = Reference().apply { this.id = "Diagnosis-1" } + } + ) } - mtbFile.pseudonymizeWith(pseudonymizeService) - mtbFile.anonymizeContentWith(pseudonymizeService) + 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) + } } 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 8ee19bc..7da0247 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/pseudonym/PseudonymizeServiceTest.kt @@ -21,6 +21,8 @@ package dev.dnpm.etl.processor.pseudonym import dev.dnpm.etl.processor.config.PseudonymizeConfigProperties import dev.pcvolkmer.mv64e.mtb.* +import java.time.Instant +import java.util.* import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -29,72 +31,67 @@ import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.doAnswer import org.mockito.kotlin.whenever -import java.time.Instant -import java.util.* @ExtendWith(MockitoExtension::class) class PseudonymizeServiceTest { - private val mtbFile = Mtb.builder() - .patient( - Patient.builder() - .id("123") - .build() - ) - .episodesOfCare( - listOf( - MtbEpisodeOfCare.builder() - .id("1") - .patient(Reference.builder().id("123").build()) - .period(PeriodDate.builder().start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))).build()) - .build() - ) - ) - .build() - - @Test - fun shouldNotUsePseudonymPrefixForGpas(@Mock generator: GpasPseudonymGenerator) { - doAnswer { - it.arguments[0] - }.whenever(generator).generate(anyString()) - - val pseudonymizeService = PseudonymizeService(generator, PseudonymizeConfigProperties()) - - mtbFile.pseudonymizeWith(pseudonymizeService) - - assertThat(mtbFile.patient.id).isEqualTo("123") - } + private val mtbFile = + Mtb.builder() + .patient(Patient.builder().id("123").build()) + .episodesOfCare( + listOf( + MtbEpisodeOfCare.builder() + .id("1") + .patient(Reference.builder().id("123").build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))) + .build() + ) + .build() + ) + ) + .build() - @Test - fun sanitizeFileName() { - val result = GpasPseudonymGenerator.sanitizeValue("l://a\\bs;1*2?3>") + @Test + fun shouldNotUsePseudonymPrefixForGpas(@Mock generator: GpasPseudonymGenerator) { + doAnswer { it.arguments[0] }.whenever(generator).generate(anyString()) - assertThat(result).isEqualTo("l___a_bs_1_2_3_") - } + val pseudonymizeService = PseudonymizeService(generator, PseudonymizeConfigProperties()) - @Test - fun shouldUsePseudonymPrefixForBuiltin(@Mock generator: AnonymizingGenerator) { - doAnswer { - it.arguments[0] - }.whenever(generator).generate(anyString()) + mtbFile.pseudonymizeWith(pseudonymizeService) - val pseudonymizeService = PseudonymizeService(generator, PseudonymizeConfigProperties()) + assertThat(mtbFile.patient.id).isEqualTo("123") + } - mtbFile.pseudonymizeWith(pseudonymizeService) + @Test + fun sanitizeFileName() { + val result = GpasPseudonymGenerator.sanitizeValue("l://a\\bs;1*2?3>") - assertThat(mtbFile.patient.id).isEqualTo("UNKNOWN_123") - } + assertThat(result).isEqualTo("l___a_bs_1_2_3_") + } + + @Test + fun shouldUsePseudonymPrefixForBuiltin(@Mock generator: AnonymizingGenerator) { + doAnswer { it.arguments[0] }.whenever(generator).generate(anyString()) + + val pseudonymizeService = PseudonymizeService(generator, PseudonymizeConfigProperties()) + + mtbFile.pseudonymizeWith(pseudonymizeService) + + assertThat(mtbFile.patient.id).isEqualTo("UNKNOWN_123") + } - @Test - fun shouldReturnDifferentValues() { - val ag = AnonymizingGenerator() + @Test + fun shouldReturnDifferentValues() { + val ag = AnonymizingGenerator() - val tans = HashSet<String>() + val tans = HashSet<String>() - (1..1000).forEach { i -> - val tan = ag.generateGenomDeTan("12345") - assertThat(tan).hasSize(64) - assertThat(tans.add(tan)).`as`("never the same result!").isTrue - } + (1..1000).forEach { i -> + val tan = ag.generateGenomDeTan("12345") + assertThat(tan).hasSize(64) + assertThat(tans.add(tan)).`as`("never the same result!").isTrue } + } } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt index b93e9f5..e9a1650 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/security/TokenServiceTest.kt @@ -19,6 +19,8 @@ package dev.dnpm.etl.processor.security +import java.util.* +import java.util.function.Consumer import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -30,124 +32,128 @@ import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.* import org.springframework.security.crypto.password.PasswordEncoder import org.springframework.security.provisioning.InMemoryUserDetailsManager -import java.util.* -import java.util.function.Consumer @ExtendWith(MockitoExtension::class) class TokenServiceTest { - private lateinit var userDetailsManager: InMemoryUserDetailsManager - private lateinit var passwordEncoder: PasswordEncoder - private lateinit var tokenRepository: TokenRepository + private lateinit var userDetailsManager: InMemoryUserDetailsManager + private lateinit var passwordEncoder: PasswordEncoder + private lateinit var tokenRepository: TokenRepository - private lateinit var tokenService: TokenService + private lateinit var tokenService: TokenService - @BeforeEach - fun setup( - @Mock userDetailsManager: InMemoryUserDetailsManager, - @Mock passwordEncoder: PasswordEncoder, - @Mock tokenRepository: TokenRepository - ) { - this.userDetailsManager = userDetailsManager - this.passwordEncoder = passwordEncoder - this.tokenRepository = tokenRepository + @BeforeEach + fun setup( + @Mock userDetailsManager: InMemoryUserDetailsManager, + @Mock passwordEncoder: PasswordEncoder, + @Mock tokenRepository: TokenRepository, + ) { + this.userDetailsManager = userDetailsManager + this.passwordEncoder = passwordEncoder + this.tokenRepository = tokenRepository - this.tokenService = TokenService(userDetailsManager, passwordEncoder, tokenRepository) - } + this.tokenService = TokenService(userDetailsManager, passwordEncoder, tokenRepository) + } - @Test - fun shouldEncodePasswordForNewToken() { - doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString()) + @Test + fun shouldEncodePasswordForNewToken() { + doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString()) - val actual = this.tokenService.addToken("Test Token") + val actual = this.tokenService.addToken("Test Token") - assertThat(actual).satisfies( + assertThat(actual) + .satisfies( Consumer { assertThat(it.isSuccess).isTrue() }, - Consumer { assertThat(it.getOrNull()).matches("testtoken:[A-Za-z0-9]{48}$") } + Consumer { assertThat(it.getOrNull()).matches("testtoken:[A-Za-z0-9]{48}$") }, ) - } + } - @Test - fun shouldContainAlphanumTokenUserPart() { - doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString()) + @Test + fun shouldContainAlphanumTokenUserPart() { + doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString()) - val actual = this.tokenService.addToken("Test Token") + val actual = this.tokenService.addToken("Test Token") - assertThat(actual).satisfies( + assertThat(actual) + .satisfies( Consumer { assertThat(it.isSuccess).isTrue() }, - Consumer { assertThat(it.getOrDefault("")).startsWith("testtoken:") } + Consumer { assertThat(it.getOrDefault("")).startsWith("testtoken:") }, ) - } + } - @Test - fun shouldNotAllowSameTokenUserPartTwice() { - doReturn(true).whenever(userDetailsManager).userExists(anyString()) + @Test + fun shouldNotAllowSameTokenUserPartTwice() { + doReturn(true).whenever(userDetailsManager).userExists(anyString()) - val actual = this.tokenService.addToken("Test Token") + val actual = this.tokenService.addToken("Test Token") - assertThat(actual).satisfies(Consumer { assertThat(it.isFailure).isTrue() }) - verify(tokenRepository, never()).save(any()) - } + assertThat(actual).satisfies(Consumer { assertThat(it.isFailure).isTrue() }) + verify(tokenRepository, never()).save(any()) + } - @Test - fun shouldSaveNewToken() { - doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString()) + @Test + fun shouldSaveNewToken() { + doAnswer { "{test}verysecret" }.whenever(passwordEncoder).encode(anyString()) - val actual = this.tokenService.addToken("Test Token") + val actual = this.tokenService.addToken("Test Token") - val captor = argumentCaptor<Token>() - verify(tokenRepository, times(1)).save(captor.capture()) + val captor = argumentCaptor<Token>() + verify(tokenRepository, times(1)).save(captor.capture()) - assertThat(actual).satisfies(Consumer { assertThat(it.isSuccess).isTrue() }) - assertThat(captor.firstValue).satisfies( + assertThat(actual).satisfies(Consumer { assertThat(it.isSuccess).isTrue() }) + assertThat(captor.firstValue) + .satisfies( Consumer { assertThat(it.name).isEqualTo("Test Token") }, Consumer { assertThat(it.username).isEqualTo("testtoken") }, - Consumer { assertThat(it.password).isEqualTo("{test}verysecret") } + Consumer { assertThat(it.password).isEqualTo("{test}verysecret") }, ) - } - - @Test - fun shouldDeleteExistingToken() { - doAnswer { - val id = it.arguments[0] as Long - Optional.of(Token(id, "Test Token", "testtoken", "{test}hsdajfgadskjhfgsdkfjg")) - }.whenever(tokenRepository).findById(anyLong()) - - this.tokenService.deleteToken(42) - - val stringCaptor = argumentCaptor<String>() - verify(userDetailsManager, times(1)).deleteUser(stringCaptor.capture()) - assertThat(stringCaptor.firstValue).isEqualTo("testtoken") - - val tokenCaptor = argumentCaptor<Token>() - verify(tokenRepository, times(1)).delete(tokenCaptor.capture()) - assertThat(tokenCaptor.firstValue.id).isEqualTo(42) - } - - @Test - fun shouldReturnAllTokensFromRepository() { - val expected = listOf( + } + + @Test + fun shouldDeleteExistingToken() { + doAnswer { + val id = it.arguments[0] as Long + Optional.of(Token(id, "Test Token", "testtoken", "{test}hsdajfgadskjhfgsdkfjg")) + } + .whenever(tokenRepository) + .findById(anyLong()) + + this.tokenService.deleteToken(42) + + val stringCaptor = argumentCaptor<String>() + verify(userDetailsManager, times(1)).deleteUser(stringCaptor.capture()) + assertThat(stringCaptor.firstValue).isEqualTo("testtoken") + + val tokenCaptor = argumentCaptor<Token>() + verify(tokenRepository, times(1)).delete(tokenCaptor.capture()) + assertThat(tokenCaptor.firstValue.id).isEqualTo(42) + } + + @Test + fun shouldReturnAllTokensFromRepository() { + val expected = + listOf( Token(1, "Test Token 1", "testtoken1", "{test}hsdajfgadskjhfgsdkfjg"), - Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd") + Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd"), ) - doReturn(expected).whenever(tokenRepository).findAll() + doReturn(expected).whenever(tokenRepository).findAll() - assertThat(tokenService.findAll()).isEqualTo(expected) - } + assertThat(tokenService.findAll()).isEqualTo(expected) + } - @Test - fun shouldAddAllTokensFromRepositoryToUserDataManager() { - val expected = listOf( + @Test + fun shouldAddAllTokensFromRepositoryToUserDataManager() { + val expected = + listOf( Token(1, "Test Token 1", "testtoken1", "{test}hsdajfgadskjhfgsdkfjg"), - Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd") + Token(2, "Test Token 2", "testtoken2", "{test}asdasdasdasdasdasdasd"), ) - doReturn(expected).whenever(tokenRepository).findAll() - - tokenService.setup() + doReturn(expected).whenever(tokenRepository).findAll() - verify(userDetailsManager, times(expected.size)).createUser(any()) - } + tokenService.setup() -}
\ No newline at end of file + verify(userDetailsManager, times(expected.size)).createUser(any()) + } +} diff --git a/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt index 39ba7c0..7743069 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/security/UserRoleServiceTest.kt @@ -19,6 +19,8 @@ package dev.dnpm.etl.processor.security +import java.time.Instant +import java.util.* import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Nested @@ -32,161 +34,148 @@ import org.springframework.security.core.session.SessionRegistry import org.springframework.security.oauth2.core.oidc.OidcIdToken import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser import org.springframework.security.oauth2.core.oidc.user.OidcUser -import java.time.Instant -import java.util.* @ExtendWith(MockitoExtension::class) class UserRoleServiceTest { - private lateinit var userRoleRepository: UserRoleRepository - private lateinit var sessionRegistry: SessionRegistry + private lateinit var userRoleRepository: UserRoleRepository + private lateinit var sessionRegistry: SessionRegistry - private lateinit var userRoleService: UserRoleService + private lateinit var userRoleService: UserRoleService - @BeforeEach - fun setup( - @Mock userRoleRepository: UserRoleRepository, - @Mock sessionRegistry: SessionRegistry - ) { - this.userRoleRepository = userRoleRepository - this.sessionRegistry = sessionRegistry - - this.userRoleService = UserRoleService(userRoleRepository, sessionRegistry) - } + @BeforeEach + fun setup(@Mock userRoleRepository: UserRoleRepository, @Mock sessionRegistry: SessionRegistry) { + this.userRoleRepository = userRoleRepository + this.sessionRegistry = sessionRegistry - @Test - fun shouldDelegateFindAllToRepository() { - userRoleService.findAll() + this.userRoleService = UserRoleService(userRoleRepository, sessionRegistry) + } - verify(userRoleRepository, times(1)).findAll() - } + @Test + fun shouldDelegateFindAllToRepository() { + userRoleService.findAll() - @Nested - inner class WithExistingUserRole { + verify(userRoleRepository, times(1)).findAll() + } - @BeforeEach - fun setup() { - doAnswer { invocation -> - Optional.of( - UserRole(invocation.getArgument(0), "patrick.tester", Role.USER) - ) - }.whenever(userRoleRepository).findById(any<Long>()) + @Nested + inner class WithExistingUserRole { - doAnswer { _ -> - listOf( - dummyPrincipal() - ) - }.whenever(sessionRegistry).allPrincipals - } + @BeforeEach + fun setup() { + doAnswer { invocation -> + Optional.of(UserRole(invocation.getArgument(0), "patrick.tester", Role.USER)) + } + .whenever(userRoleRepository) + .findById(any<Long>()) + + doAnswer { _ -> listOf(dummyPrincipal()) }.whenever(sessionRegistry).allPrincipals + } - @Test - fun shouldUpdateUserRole() { - userRoleService.updateUserRole(1, Role.ADMIN) + @Test + fun shouldUpdateUserRole() { + userRoleService.updateUserRole(1, Role.ADMIN) - val userRoleCaptor = argumentCaptor<UserRole>() - verify(userRoleRepository, times(1)).save(userRoleCaptor.capture()) + val userRoleCaptor = argumentCaptor<UserRole>() + verify(userRoleRepository, times(1)).save(userRoleCaptor.capture()) - assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L) - assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.ADMIN) - } + assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L) + assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.ADMIN) + } - @Test - fun shouldExpireSessionOnUpdate() { - val dummySessions = dummySessions() - whenever(sessionRegistry.getAllSessions(any(), any<Boolean>())).thenReturn( - dummySessions - ) + @Test + fun shouldExpireSessionOnUpdate() { + val dummySessions = dummySessions() + whenever(sessionRegistry.getAllSessions(any(), any<Boolean>())).thenReturn(dummySessions) - assertThat(dummySessions.filter { it.isExpired }).hasSize(0) + assertThat(dummySessions.filter { it.isExpired }).hasSize(0) - userRoleService.updateUserRole(1, Role.ADMIN) + userRoleService.updateUserRole(1, Role.ADMIN) - verify(sessionRegistry, times(1)).getAllSessions(any<OidcUser>(), any<Boolean>()) + verify(sessionRegistry, times(1)).getAllSessions(any<OidcUser>(), any<Boolean>()) - assertThat(dummySessions.filter { it.isExpired }).hasSize(2) - } + assertThat(dummySessions.filter { it.isExpired }).hasSize(2) + } - @Test - fun shouldDeleteUserRole() { - userRoleService.deleteUserRole(1) + @Test + fun shouldDeleteUserRole() { + userRoleService.deleteUserRole(1) - val userRoleCaptor = argumentCaptor<UserRole>() - verify(userRoleRepository, times(1)).delete(userRoleCaptor.capture()) + val userRoleCaptor = argumentCaptor<UserRole>() + verify(userRoleRepository, times(1)).delete(userRoleCaptor.capture()) - assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L) - assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.USER) - } + assertThat(userRoleCaptor.firstValue.id).isEqualTo(1L) + assertThat(userRoleCaptor.firstValue.role).isEqualTo(Role.USER) + } - @Test - fun shouldExpireSessionOnDelete() { - val dummySessions = dummySessions() - whenever(sessionRegistry.getAllSessions(any(), any<Boolean>())).thenReturn( - dummySessions - ) + @Test + fun shouldExpireSessionOnDelete() { + val dummySessions = dummySessions() + whenever(sessionRegistry.getAllSessions(any(), any<Boolean>())).thenReturn(dummySessions) - assertThat(dummySessions.filter { it.isExpired }).hasSize(0) + assertThat(dummySessions.filter { it.isExpired }).hasSize(0) - userRoleService.deleteUserRole(1) + userRoleService.deleteUserRole(1) - verify(sessionRegistry, times(1)).getAllSessions(any<OidcUser>(), any<Boolean>()) + verify(sessionRegistry, times(1)).getAllSessions(any<OidcUser>(), any<Boolean>()) - assertThat(dummySessions.filter { it.isExpired }).hasSize(2) - } + assertThat(dummySessions.filter { it.isExpired }).hasSize(2) } + } - @Nested - inner class WithoutExistingUserRole { - - @BeforeEach - fun setup() { - doAnswer { _ -> - Optional.empty<UserRole>() - }.whenever(userRoleRepository).findById(any<Long>()) - } + @Nested + inner class WithoutExistingUserRole { - @Test - fun shouldNotUpdateUserRole() { - userRoleService.updateUserRole(1, Role.ADMIN) + @BeforeEach + fun setup() { + doAnswer { _ -> Optional.empty<UserRole>() } + .whenever(userRoleRepository) + .findById(any<Long>()) + } - verify(userRoleRepository, never()).save(any<UserRole>()) - } + @Test + fun shouldNotUpdateUserRole() { + userRoleService.updateUserRole(1, Role.ADMIN) - @Test - fun shouldNotExpireSessionOnUpdate() { - userRoleService.updateUserRole(1, Role.ADMIN) + verify(userRoleRepository, never()).save(any<UserRole>()) + } - verify(sessionRegistry, never()).getAllSessions(any<OidcUser>(), any<Boolean>()) - } + @Test + fun shouldNotExpireSessionOnUpdate() { + userRoleService.updateUserRole(1, Role.ADMIN) - @Test - fun shouldNotDeleteUserRole() { - userRoleService.deleteUserRole(1) + verify(sessionRegistry, never()).getAllSessions(any<OidcUser>(), any<Boolean>()) + } - verify(userRoleRepository, never()).delete(any<UserRole>()) - } + @Test + fun shouldNotDeleteUserRole() { + userRoleService.deleteUserRole(1) - @Test - fun shouldNotExpireSessionOnDelete() { - userRoleService.deleteUserRole(1) + verify(userRoleRepository, never()).delete(any<UserRole>()) + } - verify(sessionRegistry, never()).getAllSessions(any<OidcUser>(), any<Boolean>()) - } + @Test + fun shouldNotExpireSessionOnDelete() { + userRoleService.deleteUserRole(1) + verify(sessionRegistry, never()).getAllSessions(any<OidcUser>(), any<Boolean>()) } + } - - companion object { - private fun dummyPrincipal() = DefaultOidcUser( + companion object { + private fun dummyPrincipal() = + DefaultOidcUser( listOf(), OidcIdToken( "anytokenvalue", Instant.now(), Instant.now().plusSeconds(10), - mapOf("sub" to "testsub", "preferred_username" to "patrick.tester") - ) + mapOf("sub" to "testsub", "preferred_username" to "patrick.tester"), + ), ) - private fun dummySessions() = listOf( + private fun dummySessions() = + listOf( SessionInformation( dummyPrincipal(), "SESSIONID1", @@ -196,7 +185,7 @@ class UserRoleServiceTest { dummyPrincipal(), "SESSIONID2", Date.from(Instant.now()), - ) + ), ) - } -}
\ No newline at end of file + } +} 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 bbc8b1a..3e54e71 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ConsentProcessorTest.kt @@ -10,6 +10,11 @@ import dev.dnpm.etl.processor.consent.GicsConsentService import dev.pcvolkmer.mv64e.mtb.Mtb import dev.pcvolkmer.mv64e.mtb.MvhSubmissionType import dev.pcvolkmer.mv64e.mtb.Patient +import java.io.IOException +import java.io.InputStream +import java.time.Instant +import java.time.OffsetDateTime +import java.util.* import org.assertj.core.api.Assertions.assertThat import org.hl7.fhir.r4.model.Bundle import org.hl7.fhir.r4.model.CodeableConcept @@ -28,205 +33,194 @@ 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("https://gics.example.com") - val jacksonConfig = JacksonConfig() - this.objectMapper = jacksonConfig.objectMapper() - this.fhirContext = JacksonConfig.fhirContext() - this.gicsConsentService = gicsConsentService - this.appConfigProperties = AppConfigProperties(emptyList()) - this.consentProcessor = - ConsentProcessor( - appConfigProperties, - gIcsConfigProperties, - objectMapper, - fhirContext, - gicsConsentService - ) + 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("https://gics.example.com") + val jacksonConfig = JacksonConfig() + this.objectMapper = jacksonConfig.objectMapper() + this.fhirContext = JacksonConfig.fhirContext() + this.gicsConsentService = gicsConsentService + this.appConfigProperties = AppConfigProperties(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.BROAD_CONSENT)) + + doAnswer { Bundle() } + .whenever(gicsConsentService) + .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E)) + + 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).isNotEmpty + } + + 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-08-15T00: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-08-15T00: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-08-15T00: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 } - - @Test - fun consentOk() { - assertThat(consentProcessor.toString()).isNotNull - // prep gICS response - doAnswer { getDummyBroadConsentBundle() }.whenever(gicsConsentService) - .getConsent(any(), any(), eq(ConsentDomain.BROAD_CONSENT)) - - doAnswer { Bundle() }.whenever(gicsConsentService) - .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E)) - - 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).isNotEmpty + } + + @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-08-15T00: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-08-15T00: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-08-15T00: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-08-15T00: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-08-15T00:00:00+02:00,NULL,date is after end", + "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-08-15T00:00:00+02:00,DENY,provision is denied", + "unknownCode,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-08-15T00:00:00+02:00,NULL,code does not exist - therefore expect NULL", + "2.16.840.1.113883.3.1937.777.24.5.3.8,XXXX,2025-08-15T00:00:00+02:00,NULL,system not found - therefore expect NULL", + ) + 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!!)) + } + + @Test + fun getProvisionTypeOnEmptyConsent() { + val emptyResources = Bundle().addEntry(Bundle.BundleEntryComponent().setResource(Consent())) + + val requestDate = Date.from(OffsetDateTime.parse("2025-08-15T00:00:00+02:00").toInstant()) + + val result: Consent.ConsentProvisionType = + consentProcessor.getProvisionTypeByPolicyCode( + emptyResources, + "anyCode", + "anySystem", + requestDate, + ) + assertThat(result).isNotNull() + + assertThat(result) + .`as`("empty consent resource - expect NULL") + .isEqualTo(Consent.ConsentProvisionType.NULL) + } + + fun getDummyBroadConsentBundle(): Bundle { + val bundle: InputStream? + try { + bundle = ClassPathResource("fake_broadConsent_gics_response_permit.json").getInputStream() + } catch (e: IOException) { + throw RuntimeException(e) } - 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-08-15T00: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-08-15T00: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-08-15T00: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 + return FhirContext.forR4().newJsonParser().parseResource<Bundle>(Bundle::class.java, bundle) + } + + @ParameterizedTest + @ValueSource(booleans = [true, false]) + fun mvSubmissionTypeIsSet(isTestSubmission: Boolean) { + appConfigProperties.genomDeTestSubmission = isTestSubmission + val fixture = + ConsentProcessor( + appConfigProperties, + gIcsConfigProperties, + objectMapper, + fhirContext, + gicsConsentService, + ) + + doAnswer { getDummyBroadConsentBundle() } + .whenever(gicsConsentService) + .getConsent(any(), any(), eq(ConsentDomain.BROAD_CONSENT)) + + doAnswer { + Bundle().addEntry(Bundle.BundleEntryComponent().setResource(getDummyGenomDeConsent())) } + .whenever(gicsConsentService) + .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E)) + + val inputMtb = + Mtb.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) + else { + assertThat(inputMtb.metadata.type).isEqualTo(MvhSubmissionType.INITIAL) } - - @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-08-15T00: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-08-15T00: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-08-15T00: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-08-15T00: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-08-15T00:00:00+02:00,NULL,date is after end", - "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-08-15T00:00:00+02:00,DENY,provision is denied", - "unknownCode,urn:oid:2.16.840.1.113883.3.1937.777.24.5.3,2025-08-15T00:00:00+02:00,NULL,code does not exist - therefore expect NULL", - "2.16.840.1.113883.3.1937.777.24.5.3.8,XXXX,2025-08-15T00:00:00+02:00,NULL,system not found - therefore expect NULL", - ) - 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!!)) - } - - @Test - fun getProvisionTypeOnEmptyConsent( - ) { - val emptyResources = Bundle().addEntry(Bundle.BundleEntryComponent().setResource(Consent())) - - val requestDate = Date.from(OffsetDateTime.parse("2025-08-15T00:00:00+02:00").toInstant()) - - val result: Consent.ConsentProvisionType = - consentProcessor.getProvisionTypeByPolicyCode( - emptyResources, - "anyCode", - "anySystem", - requestDate - ) - assertThat(result).isNotNull() - - - assertThat(result).`as`("empty consent resource - expect NULL") - .isEqualTo(Consent.ConsentProvisionType.NULL) - } - - 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) - } - - @ParameterizedTest - @ValueSource(booleans = [true, false]) - fun mvSubmissionTypeIsSet(isTestSubmission: Boolean) { - appConfigProperties.genomDeTestSubmission = isTestSubmission - val fixture = - ConsentProcessor( - appConfigProperties, - gIcsConfigProperties, - objectMapper, - fhirContext, - gicsConsentService - ) - - doAnswer { getDummyBroadConsentBundle() }.whenever(gicsConsentService) - .getConsent(any(), any(), eq(ConsentDomain.BROAD_CONSENT)) - - doAnswer { - Bundle().addEntry( - Bundle.BundleEntryComponent().setResource(getDummyGenomDeConsent()) - ) - }.whenever(gicsConsentService) - .getConsent(any(), any(), eq(ConsentDomain.MODELLVORHABEN_64E)) - - val inputMtb = Mtb.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) - else { - assertThat(inputMtb.metadata.type).isEqualTo(MvhSubmissionType.INITIAL) - } - - } - + } } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt index fc95808..4308fed 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ReportServiceTest.kt @@ -32,17 +32,18 @@ import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource class ReportServiceTest { - private lateinit var reportService: ReportService @BeforeEach fun setup() { - this.reportService = ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build())) + this.reportService = + ReportService(ObjectMapper().registerModule(KotlinModule.Builder().build())) } @Test fun shouldParseDataQualityReport() { - val json = """ + val json = + """ { "patient": "4711", "issues": [ @@ -52,7 +53,7 @@ class ReportServiceTest { { "severity": "fatal", "message": "Fatal Message" } ] } - """.trimIndent() + """.trimIndent() val actual = this.reportService.deserialize(json) @@ -71,7 +72,10 @@ class ReportServiceTest { @ParameterizedTest @MethodSource("testData") - fun shouldParseDataQualityReport(json: String, requestStatus: RequestStatus) { + fun shouldParseDataQualityReport( + json: String, + requestStatus: RequestStatus, + ) { val actual = this.reportService.deserialize(json) assertThat(actual.asRequestStatus()).isEqualTo(requestStatus) } @@ -88,74 +92,71 @@ class ReportServiceTest { } companion object { - @JvmStatic - fun testData(): Set<Arguments> { - return setOf( + fun testData(): Set<Arguments> = + setOf( Arguments.of( """ - { - "patient": "4711", - "issues": [ - { "severity": "info", "message": "Info Message" }, - { "severity": "warning", "message": "Warning Message" }, - { "severity": "error", "message": "Error Message" }, - { "severity": "fatal", "message": "Fatal Message" } - ] - } + { + "patient": "4711", + "issues": [ + { "severity": "info", "message": "Info Message" }, + { "severity": "warning", "message": "Warning Message" }, + { "severity": "error", "message": "Error Message" }, + { "severity": "fatal", "message": "Fatal Message" } + ] + } """.trimIndent(), - RequestStatus.ERROR + RequestStatus.ERROR, ), Arguments.of( """ - { - "patient": "4711", - "issues": [ - { "severity": "info", "message": "Info Message" }, - { "severity": "warning", "message": "Warning Message" }, - { "severity": "error", "message": "Error Message" } - ] - } + { + "patient": "4711", + "issues": [ + { "severity": "info", "message": "Info Message" }, + { "severity": "warning", "message": "Warning Message" }, + { "severity": "error", "message": "Error Message" } + ] + } """.trimIndent(), - RequestStatus.ERROR + RequestStatus.ERROR, ), Arguments.of( """ - { - "patient": "4711", - "issues": [ - { "severity": "error", "message": "Error Message" } - { "severity": "info", "message": "Info Message" } - ] - } + { + "patient": "4711", + "issues": [ + { "severity": "error", "message": "Error Message" } + { "severity": "info", "message": "Info Message" } + ] + } """.trimIndent(), - RequestStatus.ERROR + RequestStatus.ERROR, ), Arguments.of( """ - { - "patient": "4711", - "issues": [ - { "severity": "info", "message": "Info Message" }, - { "severity": "warning", "message": "Warning Message" } - ] - } + { + "patient": "4711", + "issues": [ + { "severity": "info", "message": "Info Message" }, + { "severity": "warning", "message": "Warning Message" } + ] + } """.trimIndent(), - RequestStatus.WARNING + RequestStatus.WARNING, ), Arguments.of( """ - { - "patient": "4711", - "issues": [ - { "severity": "info", "message": "Info Message" } - ] - } + { + "patient": "4711", + "issues": [ + { "severity": "info", "message": "Info Message" } + ] + } """.trimIndent(), - RequestStatus.SUCCESS - ) + RequestStatus.SUCCESS, + ), ) - } } - -}
\ 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 0a42b9b..4bd3fc1 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestProcessorTest.kt @@ -35,6 +35,8 @@ import dev.dnpm.etl.processor.output.RestMtbFileSender import dev.dnpm.etl.processor.pseudonym.PseudonymizeService import dev.dnpm.etl.processor.randomRequestId import dev.pcvolkmer.mv64e.mtb.* +import java.time.Instant +import java.util.* import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -47,42 +49,40 @@ import org.mockito.kotlin.anyValueClass import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.whenever import org.springframework.context.ApplicationEventPublisher -import java.time.Instant -import java.util.* - @ExtendWith(MockitoExtension::class) class RequestProcessorTest { - 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 - - @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 - - val objectMapper = ObjectMapper() - - requestProcessor = RequestProcessor( + 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 + + @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 + + val objectMapper = ObjectMapper() + + requestProcessor = + RequestProcessor( pseudonymizeService, transformationService, sender, @@ -90,224 +90,228 @@ class RequestProcessorTest { objectMapper, applicationEventPublisher, appConfigProperties, - consentProcessor + consentProcessor, ) - } - - @Test - fun testShouldSendMtbFileDuplicationAndSaveUnknownRequestStatusAtFirst() { - doAnswer { - Request( - 1L, - randomRequestId(), - PatientPseudonym("TEST_12345678901"), - PatientId("P1"), - Fingerprint("6vkiti5bk6ikwifpajpt7cygmd3dvw54d6lwfhzlynb3pqtzferq"), - RequestType.MTB_FILE, - RequestStatus.SUCCESS, - Instant.parse("2023-08-08T02:00:00Z") - ) - }.whenever(requestService).lastMtbFileRequestForPatientPseudonym(anyValueClass()) - - doAnswer { - false - }.whenever(requestService).isLastRequestWithKnownStatusDeletion(anyValueClass()) - - doAnswer { - it.arguments[0] as String - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - doAnswer { - it.arguments[0] - }.whenever(transformationService).transform(any<Mtb>()) - - whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) - - val mtbFile = Mtb.builder() - .patient( - Patient.builder() - .id("123") - .build() - ) + } + + @Test + fun testShouldSendMtbFileDuplicationAndSaveUnknownRequestStatusAtFirst() { + doAnswer { + Request( + 1L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("6vkiti5bk6ikwifpajpt7cygmd3dvw54d6lwfhzlynb3pqtzferq"), + RequestType.MTB_FILE, + RequestStatus.SUCCESS, + Instant.parse("2023-08-08T02:00:00Z"), + ) + } + .whenever(requestService) + .lastMtbFileRequestForPatientPseudonym(anyValueClass()) + + doAnswer { false } + .whenever(requestService) + .isLastRequestWithKnownStatusDeletion(anyValueClass()) + + doAnswer { it.arguments[0] as String } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + + whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) + + val mtbFile = + Mtb.builder() + .patient(Patient.builder().id("123").build()) .episodesOfCare( listOf( MtbEpisodeOfCare.builder() .id("1") .patient(Reference.builder().id("123").build()) - .period(PeriodDate.builder().start(Date.from(Instant.parse("2023-08-08T02:00:00.00Z"))).build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2023-08-08T02: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) - } - - @Test - fun testShouldDetectMtbFileDuplicationAndSendDuplicationEvent() { - doAnswer { - Request( - 1L, - randomRequestId(), - PatientPseudonym("TEST_12345678901"), - PatientId("P1"), - Fingerprint("4gcjwtjjtcczybsljxepdfpkaeusvd7g3vogfqpmphyffyzfx7dq"), - RequestType.MTB_FILE, - RequestStatus.SUCCESS, - Instant.parse("2023-08-08T02:00:00Z") - ) - }.whenever(requestService).lastMtbFileRequestForPatientPseudonym(anyValueClass()) - - doAnswer { - false - }.whenever(requestService).isLastRequestWithKnownStatusDeletion(anyValueClass()) - - doAnswer { - it.arguments[0] as String - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - doAnswer { - it.arguments[0] - }.whenever(transformationService).transform(any<Mtb>()) - - whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) - - val mtbFile = Mtb.builder() - .patient( - Patient.builder() - .id("123") - .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) + } + + @Test + fun testShouldDetectMtbFileDuplicationAndSendDuplicationEvent() { + doAnswer { + Request( + 1L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("4gcjwtjjtcczybsljxepdfpkaeusvd7g3vogfqpmphyffyzfx7dq"), + RequestType.MTB_FILE, + RequestStatus.SUCCESS, + Instant.parse("2023-08-08T02:00:00Z"), + ) + } + .whenever(requestService) + .lastMtbFileRequestForPatientPseudonym(anyValueClass()) + + doAnswer { false } + .whenever(requestService) + .isLastRequestWithKnownStatusDeletion(anyValueClass()) + + doAnswer { it.arguments[0] as String } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + + whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) + + val mtbFile = + Mtb.builder() + .patient(Patient.builder().id("123").build()) .episodesOfCare( listOf( MtbEpisodeOfCare.builder() .id("1") .patient(Reference.builder().id("123").build()) - .period(PeriodDate.builder().start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))).build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))) + .build() + ) .build() ) ) .build() - this.requestProcessor.processMtbFile(mtbFile) - - val eventCaptor = argumentCaptor<ResponseEvent>() - verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) - assertThat(eventCaptor.firstValue).isNotNull - assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.DUPLICATION) - } - - @Test - fun testShouldSendMtbFileAndSendSuccessEvent() { - doAnswer { - Request( - 1L, - randomRequestId(), - PatientPseudonym("TEST_12345678901"), - PatientId("P1"), - Fingerprint("different"), - RequestType.MTB_FILE, - RequestStatus.SUCCESS, - Instant.parse("2023-08-08T02:00:00Z") - ) - }.whenever(requestService).lastMtbFileRequestForPatientPseudonym(anyValueClass()) - - doAnswer { - false - }.whenever(requestService).isLastRequestWithKnownStatusDeletion(anyValueClass()) - - doAnswer { - MtbFileSender.Response(status = RequestStatus.SUCCESS) - }.whenever(sender).send(any<DnpmV2MtbFileRequest>()) - - doAnswer { - it.arguments[0] as String - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - doAnswer { - it.arguments[0] - }.whenever(transformationService).transform(any<Mtb>()) - - whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) - - val mtbFile = Mtb.builder() - .patient( - Patient.builder() - .id("123") - .build() - ) + this.requestProcessor.processMtbFile(mtbFile) + + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue).isNotNull + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.DUPLICATION) + } + + @Test + fun testShouldSendMtbFileAndSendSuccessEvent() { + doAnswer { + Request( + 1L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("different"), + RequestType.MTB_FILE, + RequestStatus.SUCCESS, + Instant.parse("2023-08-08T02:00:00Z"), + ) + } + .whenever(requestService) + .lastMtbFileRequestForPatientPseudonym(anyValueClass()) + + doAnswer { false } + .whenever(requestService) + .isLastRequestWithKnownStatusDeletion(anyValueClass()) + + doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } + .whenever(sender) + .send(any<DnpmV2MtbFileRequest>()) + + doAnswer { it.arguments[0] as String } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + + whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) + + val mtbFile = + Mtb.builder() + .patient(Patient.builder().id("123").build()) .episodesOfCare( listOf( MtbEpisodeOfCare.builder() .id("1") .patient(Reference.builder().id("123").build()) - .period(PeriodDate.builder().start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))).build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))) + .build() + ) .build() ) ) .build() - this.requestProcessor.processMtbFile(mtbFile) - - val eventCaptor = argumentCaptor<ResponseEvent>() - verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) - assertThat(eventCaptor.firstValue).isNotNull - assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) - } - - @Test - fun testShouldSendMtbFileAndSendErrorEvent() { - doAnswer { - Request( - 1L, - randomRequestId(), - PatientPseudonym("TEST_12345678901"), - PatientId("P1"), - Fingerprint("different"), - RequestType.MTB_FILE, - RequestStatus.SUCCESS, - Instant.parse("2023-08-08T02:00:00Z") - ) - }.whenever(requestService).lastMtbFileRequestForPatientPseudonym(anyValueClass()) - - doAnswer { - false - }.whenever(requestService).isLastRequestWithKnownStatusDeletion(anyValueClass()) - - doAnswer { - MtbFileSender.Response(status = RequestStatus.ERROR) - }.whenever(sender).send(any<DnpmV2MtbFileRequest>()) - - doAnswer { - it.arguments[0] as String - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - doAnswer { - it.arguments[0] - }.whenever(transformationService).transform(any<Mtb>()) - - whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) - - val mtbFile = Mtb.builder() - .patient( - Patient.builder() - .id("123") - .build() - ) + this.requestProcessor.processMtbFile(mtbFile) + + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue).isNotNull + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) + } + + @Test + fun testShouldSendMtbFileAndSendErrorEvent() { + doAnswer { + Request( + 1L, + randomRequestId(), + PatientPseudonym("TEST_12345678901"), + PatientId("P1"), + Fingerprint("different"), + RequestType.MTB_FILE, + RequestStatus.SUCCESS, + Instant.parse("2023-08-08T02:00:00Z"), + ) + } + .whenever(requestService) + .lastMtbFileRequestForPatientPseudonym(anyValueClass()) + + doAnswer { false } + .whenever(requestService) + .isLastRequestWithKnownStatusDeletion(anyValueClass()) + + doAnswer { MtbFileSender.Response(status = RequestStatus.ERROR) } + .whenever(sender) + .send(any<DnpmV2MtbFileRequest>()) + + doAnswer { it.arguments[0] as String } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + + whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) + + val mtbFile = + Mtb.builder() + .patient(Patient.builder().id("123").build()) .metadata( - MvhMetadata - .builder() + MvhMetadata.builder() .modelProjectConsent( - ModelProjectConsent - .builder() + ModelProjectConsent.builder() .provisions( - listOf(Provision.builder().type(ConsentProvision.PERMIT).purpose(ModelProjectConsentPurpose.SEQUENCING).build()) - ).build() + listOf( + Provision.builder() + .type(ConsentProvision.PERMIT) + .purpose(ModelProjectConsentPurpose.SEQUENCING) + .build() + ) + ) + .build() ) .build() ) @@ -316,143 +320,139 @@ class RequestProcessorTest { MtbEpisodeOfCare.builder() .id("1") .patient(Reference.builder().id("123").build()) - .period(PeriodDate.builder().start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))).build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))) + .build() + ) .build() ) ) .build() - this.requestProcessor.processMtbFile(mtbFile) - - val eventCaptor = argumentCaptor<ResponseEvent>() - verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) - assertThat(eventCaptor.firstValue).isNotNull - assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR) - } - - @Test - fun testShouldSendDeleteRequestAndSaveUnknownRequestStatusAtFirst() { - doAnswer { - "PSEUDONYM" - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - doAnswer { - MtbFileSender.Response(status = RequestStatus.UNKNOWN) - }.whenever(sender).send(any<DeleteRequest>()) - - this.requestProcessor.processDeletion( - TEST_PATIENT_ID, - isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE - ) - - val requestCaptor = argumentCaptor<Request>() - verify(requestService, times(1)).save(requestCaptor.capture()) - assertThat(requestCaptor.firstValue).isNotNull - assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) - } - - @Test - fun testShouldSendDeleteRequestAndSendSuccessEvent() { - doAnswer { - "PSEUDONYM" - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - doAnswer { - MtbFileSender.Response(status = RequestStatus.SUCCESS) - }.whenever(sender).send(any<DeleteRequest>()) - - this.requestProcessor.processDeletion( - TEST_PATIENT_ID, - isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE - ) - - val eventCaptor = argumentCaptor<ResponseEvent>() - verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) - assertThat(eventCaptor.firstValue).isNotNull - assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) - } - - @Test - fun testShouldSendDeleteRequestAndSendErrorEvent() { - doAnswer { - "PSEUDONYM" - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - doAnswer { - MtbFileSender.Response(status = RequestStatus.ERROR) - }.whenever(sender).send(any<DeleteRequest>()) - - this.requestProcessor.processDeletion( - TEST_PATIENT_ID, - isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE - ) - - val eventCaptor = argumentCaptor<ResponseEvent>() - verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) - assertThat(eventCaptor.firstValue).isNotNull - assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR) - } - - @Test - fun testShouldSendDeleteRequestWithPseudonymErrorAndSaveErrorRequestStatus() { - doThrow(RuntimeException()).whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - this.requestProcessor.processDeletion( - TEST_PATIENT_ID, - isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE - ) - - val requestCaptor = argumentCaptor<Request>() - verify(requestService, times(1)).save(requestCaptor.capture()) - assertThat(requestCaptor.firstValue).isNotNull - assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR) - } - - @Test - fun testShouldNotDetectMtbFileDuplicationIfDuplicationNotConfigured() { - this.appConfigProperties.duplicationDetection = false - - doAnswer { - it.arguments[0] as String - }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) - - doAnswer { - it.arguments[0] - }.whenever(transformationService).transform(any<Mtb>()) - - doAnswer { - MtbFileSender.Response(status = RequestStatus.SUCCESS) - }.whenever(sender).send(any<DnpmV2MtbFileRequest>()) - - whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) - - val mtbFile = Mtb.builder() - .patient( - Patient.builder() - .id("123") - .build() - ) + this.requestProcessor.processMtbFile(mtbFile) + + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue).isNotNull + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR) + } + + @Test + fun testShouldSendDeleteRequestAndSaveUnknownRequestStatusAtFirst() { + doAnswer { "PSEUDONYM" }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) + + doAnswer { MtbFileSender.Response(status = RequestStatus.UNKNOWN) } + .whenever(sender) + .send(any<DeleteRequest>()) + + this.requestProcessor.processDeletion( + TEST_PATIENT_ID, + isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE, + ) + + val requestCaptor = argumentCaptor<Request>() + verify(requestService, times(1)).save(requestCaptor.capture()) + assertThat(requestCaptor.firstValue).isNotNull + assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.UNKNOWN) + } + + @Test + fun testShouldSendDeleteRequestAndSendSuccessEvent() { + doAnswer { "PSEUDONYM" }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) + + doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } + .whenever(sender) + .send(any<DeleteRequest>()) + + this.requestProcessor.processDeletion( + TEST_PATIENT_ID, + isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE, + ) + + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue).isNotNull + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) + } + + @Test + fun testShouldSendDeleteRequestAndSendErrorEvent() { + doAnswer { "PSEUDONYM" }.whenever(pseudonymizeService).patientPseudonym(anyValueClass()) + + doAnswer { MtbFileSender.Response(status = RequestStatus.ERROR) } + .whenever(sender) + .send(any<DeleteRequest>()) + + this.requestProcessor.processDeletion( + TEST_PATIENT_ID, + isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE, + ) + + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue).isNotNull + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR) + } + + @Test + fun testShouldSendDeleteRequestWithPseudonymErrorAndSaveErrorRequestStatus() { + doThrow(RuntimeException()).whenever(pseudonymizeService).patientPseudonym(anyValueClass()) + + this.requestProcessor.processDeletion( + TEST_PATIENT_ID, + isConsented = TtpConsentStatus.UNKNOWN_CHECK_FILE, + ) + + val requestCaptor = argumentCaptor<Request>() + verify(requestService, times(1)).save(requestCaptor.capture()) + assertThat(requestCaptor.firstValue).isNotNull + assertThat(requestCaptor.firstValue.status).isEqualTo(RequestStatus.ERROR) + } + + @Test + fun testShouldNotDetectMtbFileDuplicationIfDuplicationNotConfigured() { + this.appConfigProperties.duplicationDetection = false + + doAnswer { it.arguments[0] as String } + .whenever(pseudonymizeService) + .patientPseudonym(anyValueClass()) + + doAnswer { it.arguments[0] }.whenever(transformationService).transform(any<Mtb>()) + + doAnswer { MtbFileSender.Response(status = RequestStatus.SUCCESS) } + .whenever(sender) + .send(any<DnpmV2MtbFileRequest>()) + + whenever(consentProcessor.consentGatedCheckAndTryEmbedding(any())).thenReturn(true) + + val mtbFile = + Mtb.builder() + .patient(Patient.builder().id("123").build()) .episodesOfCare( listOf( MtbEpisodeOfCare.builder() .id("1") .patient(Reference.builder().id("123").build()) - .period(PeriodDate.builder().start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))).build()) + .period( + PeriodDate.builder() + .start(Date.from(Instant.parse("2021-01-01T00:00:00.00Z"))) + .build() + ) .build() ) ) .build() - this.requestProcessor.processMtbFile(mtbFile) - - val eventCaptor = argumentCaptor<ResponseEvent>() - verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) - assertThat(eventCaptor.firstValue).isNotNull - assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) - } + this.requestProcessor.processMtbFile(mtbFile) - companion object { - val TEST_PATIENT_ID = PatientId("TEST_12345678901") - } + val eventCaptor = argumentCaptor<ResponseEvent>() + verify(applicationEventPublisher, times(1)).publishEvent(eventCaptor.capture()) + assertThat(eventCaptor.firstValue).isNotNull + assertThat(eventCaptor.firstValue.status).isEqualTo(RequestStatus.SUCCESS) + } + companion object { + val TEST_PATIENT_ID = PatientId("TEST_12345678901") + } } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt index c0e4400..bc0286c 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/RequestServiceTest.kt @@ -24,6 +24,7 @@ import dev.dnpm.etl.processor.monitoring.Request import dev.dnpm.etl.processor.monitoring.RequestRepository import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.dnpm.etl.processor.monitoring.RequestType +import java.time.Instant import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -33,37 +34,37 @@ import org.mockito.Mockito.* import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.anyValueClass import org.mockito.kotlin.whenever -import java.time.Instant @ExtendWith(MockitoExtension::class) class RequestServiceTest { - private lateinit var requestRepository: RequestRepository - - private lateinit var requestService: RequestService - - private fun anyRequest() = any(Request::class.java) ?: Request( - 0L, - randomRequestId(), - PatientPseudonym("TEST_dummy"), - PatientId("PX"), - Fingerprint("dummy"), - RequestType.MTB_FILE, - RequestStatus.SUCCESS, - Instant.parse("2023-08-08T02:00:00Z") - ) - - @BeforeEach - fun setup( - @Mock requestRepository: RequestRepository - ) { - this.requestRepository = requestRepository - this.requestService = RequestService(requestRepository) - } - - @Test - fun shouldIndicateLastRequestIsDeleteRequest() { - val requests = listOf( + private lateinit var requestRepository: RequestRepository + + private lateinit var requestService: RequestService + + private fun anyRequest() = + any(Request::class.java) + ?: Request( + 0L, + randomRequestId(), + PatientPseudonym("TEST_dummy"), + PatientId("PX"), + Fingerprint("dummy"), + RequestType.MTB_FILE, + RequestStatus.SUCCESS, + Instant.parse("2023-08-08T02:00:00Z"), + ) + + @BeforeEach + fun setup(@Mock requestRepository: RequestRepository) { + this.requestRepository = requestRepository + this.requestService = RequestService(requestRepository) + } + + @Test + fun shouldIndicateLastRequestIsDeleteRequest() { + val requests = + listOf( Request( 1L, randomRequestId(), @@ -72,7 +73,7 @@ class RequestServiceTest { Fingerprint("0123456789abcdef1"), RequestType.MTB_FILE, RequestStatus.WARNING, - Instant.parse("2023-07-07T00:00:00Z") + Instant.parse("2023-07-07T00:00:00Z"), ), Request( 2L, @@ -82,7 +83,7 @@ class RequestServiceTest { Fingerprint("0123456789abcdefd"), RequestType.DELETE, RequestStatus.WARNING, - Instant.parse("2023-07-07T02:00:00Z") + Instant.parse("2023-07-07T02:00:00Z"), ), Request( 3L, @@ -92,18 +93,19 @@ class RequestServiceTest { Fingerprint("0123456789abcdef1"), RequestType.MTB_FILE, RequestStatus.UNKNOWN, - Instant.parse("2023-08-11T00:00:00Z") - ) + Instant.parse("2023-08-11T00:00:00Z"), + ), ) - val actual = RequestService.isLastRequestWithKnownStatusDeletion(requests) + val actual = RequestService.isLastRequestWithKnownStatusDeletion(requests) - assertThat(actual).isTrue() - } + assertThat(actual).isTrue() + } - @Test - fun shouldIndicateLastRequestIsNotDeleteRequest() { - val requests = listOf( + @Test + fun shouldIndicateLastRequestIsNotDeleteRequest() { + val requests = + listOf( Request( 1L, randomRequestId(), @@ -112,7 +114,7 @@ class RequestServiceTest { Fingerprint("0123456789abcdef1"), RequestType.MTB_FILE, RequestStatus.WARNING, - Instant.parse("2023-07-07T00:00:00Z") + Instant.parse("2023-07-07T00:00:00Z"), ), Request( 2L, @@ -122,7 +124,7 @@ class RequestServiceTest { Fingerprint("0123456789abcdef1"), RequestType.MTB_FILE, RequestStatus.WARNING, - Instant.parse("2023-07-07T02:00:00Z") + Instant.parse("2023-07-07T02:00:00Z"), ), Request( 3L, @@ -132,18 +134,19 @@ class RequestServiceTest { Fingerprint("0123456789abcdef1"), RequestType.MTB_FILE, RequestStatus.UNKNOWN, - Instant.parse("2023-08-11T00:00:00Z") - ) + Instant.parse("2023-08-11T00:00:00Z"), + ), ) - val actual = RequestService.isLastRequestWithKnownStatusDeletion(requests) + val actual = RequestService.isLastRequestWithKnownStatusDeletion(requests) - assertThat(actual).isFalse() - } + assertThat(actual).isFalse() + } - @Test - fun shouldReturnPatientsLastRequest() { - val requests = listOf( + @Test + fun shouldReturnPatientsLastRequest() { + val requests = + listOf( Request( 1L, randomRequestId(), @@ -152,7 +155,7 @@ class RequestServiceTest { Fingerprint("0123456789abcdef1"), RequestType.DELETE, RequestStatus.SUCCESS, - Instant.parse("2023-07-07T02:00:00Z") + Instant.parse("2023-07-07T02:00:00Z"), ), Request( 1L, @@ -162,66 +165,71 @@ class RequestServiceTest { Fingerprint("0123456789abcdef2"), RequestType.MTB_FILE, RequestStatus.WARNING, - Instant.parse("2023-08-08T00:00:00Z") - ) + Instant.parse("2023-08-08T00:00:00Z"), + ), ) - val actual = RequestService.lastMtbFileRequestForPatientPseudonym(requests) + val actual = RequestService.lastMtbFileRequestForPatientPseudonym(requests) - assertThat(actual).isInstanceOf(Request::class.java) - assertThat(actual?.fingerprint).isEqualTo(Fingerprint("0123456789abcdef2")) - } + assertThat(actual).isInstanceOf(Request::class.java) + assertThat(actual?.fingerprint).isEqualTo(Fingerprint("0123456789abcdef2")) + } - @Test - fun shouldReturnNullIfNoRequests() { - val requests = listOf<Request>() + @Test + fun shouldReturnNullIfNoRequests() { + val requests = listOf<Request>() - val actual = RequestService.lastMtbFileRequestForPatientPseudonym(requests) + val actual = RequestService.lastMtbFileRequestForPatientPseudonym(requests) - assertThat(actual).isNull() - } + assertThat(actual).isNull() + } - @Test - fun saveShouldSaveRequestUsingRepository() { - doAnswer { - val obj = it.arguments[0] as Request - obj.copy(id = 1L) - }.whenever(requestRepository).save(anyRequest()) + @Test + fun saveShouldSaveRequestUsingRepository() { + doAnswer { + val obj = it.arguments[0] as Request + obj.copy(id = 1L) + } + .whenever(requestRepository) + .save(anyRequest()) - val request = Request( + val request = + Request( randomRequestId(), PatientPseudonym("TEST_12345678901"), PatientId("P1"), Fingerprint("0123456789abcdef1"), RequestType.DELETE, RequestStatus.SUCCESS, - Instant.parse("2023-07-07T02:00:00Z") + Instant.parse("2023-07-07T02:00:00Z"), ) - requestService.save(request) - - verify(requestRepository, times(1)).save(anyRequest()) - } + requestService.save(request) - @Test - fun allRequestsByPatientPseudonymShouldRequestAllRequestsForPatientPseudonym() { - requestService.allRequestsByPatientPseudonym(PatientPseudonym("TEST_12345678901")) + verify(requestRepository, times(1)).save(anyRequest()) + } - verify(requestRepository, times(1)).findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass()) - } + @Test + fun allRequestsByPatientPseudonymShouldRequestAllRequestsForPatientPseudonym() { + requestService.allRequestsByPatientPseudonym(PatientPseudonym("TEST_12345678901")) - @Test - fun lastMtbFileRequestForPatientPseudonymShouldRequestAllRequestsForPatientPseudonym() { - requestService.lastMtbFileRequestForPatientPseudonym(PatientPseudonym("TEST_12345678901")) + verify(requestRepository, times(1)) + .findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass()) + } - verify(requestRepository, times(1)).findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass()) - } + @Test + fun lastMtbFileRequestForPatientPseudonymShouldRequestAllRequestsForPatientPseudonym() { + requestService.lastMtbFileRequestForPatientPseudonym(PatientPseudonym("TEST_12345678901")) - @Test - fun isLastRequestDeletionShouldRequestAllRequestsForPatientPseudonym() { - requestService.isLastRequestWithKnownStatusDeletion(PatientPseudonym("TEST_12345678901")) + verify(requestRepository, times(1)) + .findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass()) + } - verify(requestRepository, times(1)).findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass()) - } + @Test + fun isLastRequestDeletionShouldRequestAllRequestsForPatientPseudonym() { + requestService.isLastRequestWithKnownStatusDeletion(PatientPseudonym("TEST_12345678901")) -}
\ No newline at end of file + verify(requestRepository, times(1)) + .findAllByPatientPseudonymOrderByProcessedAtDesc(anyValueClass()) + } +} 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 465d8b8..16a5791 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/ResponseProcessorTest.kt @@ -23,6 +23,8 @@ import dev.dnpm.etl.processor.* import dev.dnpm.etl.processor.monitoring.Request import dev.dnpm.etl.processor.monitoring.RequestStatus import dev.dnpm.etl.processor.monitoring.RequestType +import java.time.Instant +import java.util.* import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -33,105 +35,92 @@ import org.mockito.Mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.kotlin.* import reactor.core.publisher.Sinks -import java.time.Instant -import java.util.* @ExtendWith(MockitoExtension::class) class ResponseProcessorTest { - private lateinit var requestService: RequestService - private lateinit var statisticsUpdateProducer: Sinks.Many<Any> - - private lateinit var responseProcessor: ResponseProcessor - - private val testRequest = Request( - 1L, - RequestId("TestID1234"), - PatientPseudonym("PSEUDONYM-A"), - PatientId("1"), - Fingerprint("dummyfingerprint"), - RequestType.MTB_FILE, - RequestStatus.UNKNOWN - ) - - @BeforeEach - fun setup( - @Mock requestService: RequestService, - @Mock statisticsUpdateProducer: Sinks.Many<Any> - ) { - this.requestService = requestService - this.statisticsUpdateProducer = statisticsUpdateProducer - - this.responseProcessor = ResponseProcessor(requestService, statisticsUpdateProducer) - } + private lateinit var requestService: RequestService + private lateinit var statisticsUpdateProducer: Sinks.Many<Any> - @Test - fun shouldNotSaveStatusForUnknownRequest() { - doAnswer { - Optional.empty<Request>() - }.whenever(requestService).findByUuid(anyValueClass()) + private lateinit var responseProcessor: ResponseProcessor - val event = ResponseEvent( - RequestId("TestID1234"), - Instant.parse("2023-09-09T00:00:00Z"), - RequestStatus.SUCCESS - ) + private val testRequest = + Request( + 1L, + RequestId("TestID1234"), + PatientPseudonym("PSEUDONYM-A"), + PatientId("1"), + Fingerprint("dummyfingerprint"), + RequestType.MTB_FILE, + RequestStatus.UNKNOWN, + ) - this.responseProcessor.handleResponseEvent(event) + @BeforeEach + fun setup(@Mock requestService: RequestService, @Mock statisticsUpdateProducer: Sinks.Many<Any>) { + this.requestService = requestService + this.statisticsUpdateProducer = statisticsUpdateProducer - verify(requestService, never()).save(any()) - } + this.responseProcessor = ResponseProcessor(requestService, statisticsUpdateProducer) + } - @Test - fun shouldNotSaveStatusWithUnknownState() { - doAnswer { - Optional.of(testRequest) - }.whenever(requestService).findByUuid(anyValueClass()) + @Test + fun shouldNotSaveStatusForUnknownRequest() { + doAnswer { Optional.empty<Request>() }.whenever(requestService).findByUuid(anyValueClass()) - val event = ResponseEvent( + val event = + ResponseEvent( RequestId("TestID1234"), Instant.parse("2023-09-09T00:00:00Z"), - RequestStatus.UNKNOWN + RequestStatus.SUCCESS, ) - this.responseProcessor.handleResponseEvent(event) + this.responseProcessor.handleResponseEvent(event) - verify(requestService, never()).save(any<Request>()) - } + verify(requestService, never()).save(any()) + } - @ParameterizedTest - @MethodSource("requestStatusSource") - fun shouldSaveStatusForKnownRequest(requestStatus: RequestStatus) { - doAnswer { - Optional.of(testRequest) - }.whenever(requestService).findByUuid(anyValueClass()) + @Test + fun shouldNotSaveStatusWithUnknownState() { + doAnswer { Optional.of(testRequest) }.whenever(requestService).findByUuid(anyValueClass()) - val event = ResponseEvent( + val event = + ResponseEvent( RequestId("TestID1234"), Instant.parse("2023-09-09T00:00:00Z"), - requestStatus + RequestStatus.UNKNOWN, ) - this.responseProcessor.handleResponseEvent(event) + 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) - } + verify(requestService, never()).save(any<Request>()) + } - companion object { + @ParameterizedTest + @MethodSource("requestStatusSource") + fun shouldSaveStatusForKnownRequest(requestStatus: RequestStatus) { + doAnswer { Optional.of(testRequest) }.whenever(requestService).findByUuid(anyValueClass()) - @JvmStatic - fun requestStatusSource(): Set<RequestStatus> { - return setOf( - RequestStatus.SUCCESS, - RequestStatus.WARNING, - RequestStatus.ERROR, - RequestStatus.DUPLICATION - ) - } + val event = + ResponseEvent(RequestId("TestID1234"), Instant.parse("2023-09-09T00:00:00Z"), requestStatus) - } + this.responseProcessor.handleResponseEvent(event) -}
\ No newline at end of file + val captor = argumentCaptor<Request>() + verify(requestService, times(1)).save(captor.capture()) + assertThat(captor.firstValue).isNotNull + assertThat(captor.firstValue.status).isEqualTo(requestStatus) + } + + companion object { + + @JvmStatic + fun requestStatusSource(): Set<RequestStatus> { + return setOf( + RequestStatus.SUCCESS, + RequestStatus.WARNING, + RequestStatus.ERROR, + RequestStatus.DUPLICATION, + ) + } + } +} 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 ba9d23f..c6438e8 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/TransformationServiceTest.kt @@ -21,106 +21,139 @@ package dev.dnpm.etl.processor.services import dev.dnpm.etl.processor.config.JacksonConfig import dev.pcvolkmer.mv64e.mtb.* +import java.time.Instant +import java.util.Date import org.assertj.core.api.Assertions.assertThat +import org.hl7.fhir.instance.model.api.IBaseResource import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.hl7.fhir.instance.model.api.IBaseResource -import java.time.Instant -import java.util.Date class TransformationServiceTest { - private lateinit var service: TransformationService + private lateinit var service: TransformationService - @BeforeEach - fun setup() { - this.service = TransformationService( - JacksonConfig().objectMapper(), listOf( + @BeforeEach + fun setup() { + this.service = + TransformationService( + JacksonConfig().objectMapper(), + listOf( Transformation.of("diagnoses[*].code.version") from "2013" to "2014", - ) + ), ) - } - - @Test - fun shouldTransformMtbFile() { - val mtbFile = Mtb.builder().diagnoses( - listOf( - MtbDiagnosis.builder().id("1234").code(Coding.builder().code("F79.9").version("2013").build()).build() + } + + @Test + fun shouldTransformMtbFile() { + val mtbFile = + Mtb.builder() + .diagnoses( + listOf( + MtbDiagnosis.builder() + .id("1234") + .code(Coding.builder().code("F79.9").version("2013").build()) + .build() + ) ) - ).build() - - val actual = this.service.transform(mtbFile) - - assertThat(actual).isNotNull - assertThat(actual.diagnoses[0].code.version).isEqualTo("2014") - } - - @Test - fun shouldOnlyTransformGivenValues() { - val mtbFile = Mtb.builder().diagnoses( - listOf( - MtbDiagnosis.builder().id("1234").code(Coding.builder().code("F79.9").version("2013").build()).build(), - MtbDiagnosis.builder().id("1234").code(Coding.builder().code("F79.8").version("2019").build()).build() + .build() + + val actual = this.service.transform(mtbFile) + + assertThat(actual).isNotNull + assertThat(actual.diagnoses[0].code.version).isEqualTo("2014") + } + + @Test + fun shouldOnlyTransformGivenValues() { + val mtbFile = + Mtb.builder() + .diagnoses( + listOf( + MtbDiagnosis.builder() + .id("1234") + .code(Coding.builder().code("F79.9").version("2013").build()) + .build(), + MtbDiagnosis.builder() + .id("1234") + .code(Coding.builder().code("F79.8").version("2019").build()) + .build(), + ) ) - ).build() - - val actual = this.service.transform(mtbFile) + .build() + + 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") + } + + @Test + fun shouldTransformConsentValues() { + val mtbFile = + Mtb.builder() + .diagnoses( + listOf( + MtbDiagnosis.builder() + .id("1234") + .code(Coding.builder().code("F79.9").version("2013").build()) + .build(), + MtbDiagnosis.builder() + .id("1234") + .code(Coding.builder().code("F79.8").version("2019").build()) + .build(), + ) + ) + .build() + + 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") + } + + @Test + fun shouldTransformConsent() { + val mvhMetadata = MvhMetadata.builder().transferTan("transfertan12345").build() + + assertThat(mvhMetadata).isNotNull + mvhMetadata.modelProjectConsent = + ModelProjectConsent.builder() + .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))) + .version("1") + .provisions( + listOf( + Provision.builder() + .type(ConsentProvision.PERMIT) + .purpose(ModelProjectConsentPurpose.SEQUENCING) + .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))) + .build(), + Provision.builder() + .type(ConsentProvision.PERMIT) + .purpose(ModelProjectConsentPurpose.REIDENTIFICATION) + .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))) + .build(), + Provision.builder() + .type(ConsentProvision.DENY) + .purpose(ModelProjectConsentPurpose.CASE_IDENTIFICATION) + .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))) + .build(), + ) + ) + .build() + val consent = ConsentProcessorTest.getDummyGenomDeConsent() - 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") - } + mvhMetadata.researchConsents = mutableListOf() + mvhMetadata.researchConsents.add(mapOf(consent.id to consent as IBaseResource)) - @Test - fun shouldTransformConsentValues() { - val mtbFile = Mtb.builder().diagnoses( - listOf( - MtbDiagnosis.builder().id("1234").code(Coding.builder().code("F79.9").version("2013").build()).build(), - MtbDiagnosis.builder().id("1234").code(Coding.builder().code("F79.8").version("2019").build()).build() - ) - ).build() - - 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") - } - - @Test - fun shouldTransformConsent() { - val mvhMetadata = MvhMetadata.builder().transferTan("transfertan12345").build() - - assertThat(mvhMetadata).isNotNull - mvhMetadata.modelProjectConsent = - ModelProjectConsent.builder().date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))) - .version("1").provisions( - listOf( - Provision.builder().type(ConsentProvision.PERMIT) - .purpose(ModelProjectConsentPurpose.SEQUENCING) - .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))).build(), - Provision.builder().type(ConsentProvision.PERMIT) - .purpose(ModelProjectConsentPurpose.REIDENTIFICATION) - .date(Date.from(Instant.parse("2025-08-15T00:00:00.00Z"))).build(), - Provision.builder().type(ConsentProvision.DENY) - .purpose(ModelProjectConsentPurpose.CASE_IDENTIFICATION) - .date(Date.from(Instant.parse("2025-08-15T00: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 - - } + val mtbFile = Mtb.builder().metadata(mvhMetadata).build() + val transformed = service.transform(mtbFile) + assertThat(transformed.metadata.modelProjectConsent.date).isNotNull + } } diff --git a/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt b/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt index 95bf41b..8d5024a 100644 --- a/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt +++ b/src/test/kotlin/dev/dnpm/etl/processor/services/kafka/KafkaResponseProcessorTest.kt @@ -39,7 +39,6 @@ import org.springframework.http.HttpStatus @ExtendWith(MockitoExtension::class) class KafkaResponseProcessorTest { - private lateinit var eventPublisher: ApplicationEventPublisher private lateinit var objectMapper: ObjectMapper @@ -48,9 +47,9 @@ class KafkaResponseProcessorTest { private fun createKafkaRecord( requestId: String, statusCode: Int = 200, - statusBody: Map<String, Any>? = mapOf() - ): ConsumerRecord<String, String> { - return ConsumerRecord<String, String>( + statusBody: Map<String, Any>? = mapOf(), + ): ConsumerRecord<String, String> = + ConsumerRecord<String, String>( "test-topic", 0, 0, @@ -58,14 +57,15 @@ class KafkaResponseProcessorTest { if (statusBody == null) { "" } else { - this.objectMapper.writeValueAsString(KafkaResponseProcessor.ResponseBody(requestId, statusCode, statusBody)) - } + this.objectMapper.writeValueAsString( + KafkaResponseProcessor.ResponseBody(requestId, statusCode, statusBody), + ) + }, ) - } @BeforeEach fun setup( - @Mock eventPublisher: ApplicationEventPublisher + @Mock eventPublisher: ApplicationEventPublisher, ) { this.eventPublisher = eventPublisher this.objectMapper = ObjectMapper().registerModule(KotlinModule.Builder().build()) @@ -75,18 +75,19 @@ class KafkaResponseProcessorTest { @Test fun shouldNotProcessRecordsWithoutRequestIdInBody() { - val record = ConsumerRecord<String, String>( - "test-topic", - 0, - 0, - null, - """ + val record = + ConsumerRecord<String, String>( + "test-topic", + 0, + 0, + null, + """ { "statusCode": 200, "statusBody": {} } - """.trimIndent() - ) + """.trimIndent(), + ) this.kafkaResponseProcessor.onMessage(record) @@ -95,19 +96,20 @@ class KafkaResponseProcessorTest { @Test fun shouldProcessRecordsWithAliasNames() { - val record = ConsumerRecord<String, String>( - "test-topic", - 0, - 0, - null, - """ + val record = + ConsumerRecord<String, String>( + "test-topic", + 0, + 0, + null, + """ { "request_id": "test0123456789", "status_code": 200, "status_body": {} } - """.trimIndent() - ) + """.trimIndent(), + ) this.kafkaResponseProcessor.onMessage(record) @@ -116,7 +118,9 @@ class KafkaResponseProcessorTest { @Test fun shouldNotProcessRecordsWithoutValidStatusBody() { - this.kafkaResponseProcessor.onMessage(createKafkaRecord(requestId = "TestID1234", statusBody = null)) + this.kafkaResponseProcessor.onMessage( + createKafkaRecord(requestId = "TestID1234", statusBody = null), + ) verify(eventPublisher, never()).publishEvent(any<ResponseEvent>()) } @@ -129,21 +133,16 @@ class KafkaResponseProcessorTest { } companion object { - @JvmStatic - fun statusCodeSource(): Set<Int> { - return setOf( + fun statusCodeSource(): Set<Int> = + setOf( HttpStatus.OK, HttpStatus.CREATED, HttpStatus.BAD_REQUEST, HttpStatus.NOT_FOUND, HttpStatus.UNPROCESSABLE_ENTITY, - HttpStatus.INTERNAL_SERVER_ERROR - ) - .map { it.value() } + HttpStatus.INTERNAL_SERVER_ERROR, + ).map { it.value() } .toSet() - } - } - -}
\ No newline at end of file +} |
