From 7b4991e7b5dc50c5fa91473dcf46e18951dc425c Mon Sep 17 00:00:00 2001 From: Ryan Amari Date: Fri, 16 Feb 2024 10:47:03 -0500 Subject: [PATCH] ALS-5905: Cleanup patient merging genomic parent, add tests for that. Cleanup a bunch of other tests --- .../genotype/BucketIndexBySampleTest.java | 3 +- .../genotype/VariantMetadataIndexTest.java | 3 +- .../GenomicProcessorParentImpl.java | 56 ++++++--- ...omicProcessorPatientMergingParentImpl.java | 56 +++++---- .../processing/AbstractProcessorTest.java | 79 +------------ .../hpds/processing/CountProcessorTest.java | 13 +-- .../GenomicProcessorParentImplTest.java | 64 +++++++++++ ...ProcessorPatientMergingParentImplTest.java | 106 ++++++++++++++++++ .../PatientVariantJoinHandlerTest.java | 14 +-- .../GenomicProcessorRestClientTest.java | 2 + .../AbstractProcessorIntegrationTest.java | 1 + 11 files changed, 272 insertions(+), 125 deletions(-) create mode 100644 processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorParentImplTest.java create mode 100644 processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorPatientMergingParentImplTest.java diff --git a/etl/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/data/genotype/BucketIndexBySampleTest.java b/etl/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/data/genotype/BucketIndexBySampleTest.java index d87686ea..d6aa3f79 100644 --- a/etl/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/data/genotype/BucketIndexBySampleTest.java +++ b/etl/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/data/genotype/BucketIndexBySampleTest.java @@ -9,6 +9,7 @@ import edu.harvard.hms.dbmi.avillach.hpds.etl.genotype.NewVCFLoader; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.test.context.event.annotation.BeforeTestClass; @@ -66,7 +67,7 @@ public class BucketIndexBySampleTest { Set variantSet; List patientSet; - @BeforeTestClass + @BeforeAll public static void initializeBinfile() throws Exception { //load variant data NewVCFLoader.main(new String[] {VCF_INDEX_FILE, STORAGE_DIR, MERGED_DIR}); diff --git a/etl/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/data/genotype/VariantMetadataIndexTest.java b/etl/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/data/genotype/VariantMetadataIndexTest.java index 476318ad..df340f84 100644 --- a/etl/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/data/genotype/VariantMetadataIndexTest.java +++ b/etl/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/data/genotype/VariantMetadataIndexTest.java @@ -8,6 +8,7 @@ import edu.harvard.hms.dbmi.avillach.hpds.data.genotype.caching.VariantBucketHolder; import edu.harvard.hms.dbmi.avillach.hpds.etl.genotype.VariantMetadataLoader; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.test.context.event.annotation.BeforeTestClass; @@ -37,7 +38,7 @@ public class VariantMetadataIndexTest { private static final String spec5 = "4,9856624,CAAAAA,CA"; private static final String spec5Info = "AC=3033;AF=6.05631e-01;NS=2504;AN=5008;EAS_AF=5.23800e-01;EUR_AF=7.54500e-01;AFR_AF=4.28900e-01;AMR_AF=7.82400e-01;SAS_AF=6.50300e-01;DP=20851;VT=INDEL"; - @BeforeTestClass + @BeforeAll public static void initializeBinfile() throws Exception { VariantMetadataLoader.main(new String[] {"./src/test/resources/test_vcfIndex.tsv", binFile, "target/VariantMetadataStorage.bin"}); diff --git a/processing/src/main/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorParentImpl.java b/processing/src/main/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorParentImpl.java index 760503d2..0806dff8 100644 --- a/processing/src/main/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorParentImpl.java +++ b/processing/src/main/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorParentImpl.java @@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; +import reactor.core.scheduler.Schedulers; import java.math.BigInteger; import java.util.*; @@ -34,12 +35,16 @@ public Set load(String conceptPath) { } }); - private List infoColumnsMeta; - - private List patientIds; + private final List infoColumnsMeta; + private final List patientIds; + private final Set infoStoreColumns; public GenomicProcessorParentImpl(List nodes) { this.nodes = nodes; + + patientIds = initializePatientIds(); + infoStoreColumns = initializeInfoStoreColumns(); + infoColumnsMeta = initInfoColumnsMeta(); } @Override @@ -83,14 +88,26 @@ public Mono> getVariantList(DistributableQuery distributableQ @Override public List getPatientIds() { - if (patientIds != null) { - return patientIds; - } else { - // todo: verify all nodes have the same potients - List result = nodes.get(0).getPatientIds(); - patientIds = result; - return result; - } + return patientIds; + } + + private List initializePatientIds() { + List patientIds = Flux.just(nodes.toArray(GenomicProcessor[]::new)) + .flatMap(node -> Mono.fromCallable(node::getPatientIds).subscribeOn(Schedulers.boundedElastic())) + .reduce((patientIds1, patientIds2) -> { + if (patientIds1.size() != patientIds2.size()) { + throw new IllegalStateException("Patient lists from partitions do not match"); + } else { + for (int i = 0; i < patientIds1.size(); i++) { + if (!patientIds1.get(i).equals(patientIds2.get(i))) { + throw new IllegalStateException("Patient lists from partitions do not match"); + } + } + } + return patientIds1; + }).block(); + + return patientIds; } @Override @@ -107,7 +124,10 @@ public Optional getMasks(String path, VariantBucketHolder getInfoStoreColumns() { - // todo: cache this + return infoStoreColumns; + } + + private Set initializeInfoStoreColumns() { return nodes.parallelStream() .map(GenomicProcessor::getInfoStoreColumns) .flatMap(Set::stream) @@ -121,10 +141,14 @@ public Set getInfoStoreValues(String conceptPath) { @Override public List getInfoColumnMeta() { - // todo: initialize on startup? - if (infoColumnsMeta == null) { - infoColumnsMeta = nodes.get(0).getInfoColumnMeta(); - } return infoColumnsMeta; } + + private List initInfoColumnsMeta() { + return nodes.parallelStream() + .map(GenomicProcessor::getInfoColumnMeta) + .map(HashSet::new) + .flatMap(Set::stream) + .collect(Collectors.toList()); + } } diff --git a/processing/src/main/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorPatientMergingParentImpl.java b/processing/src/main/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorPatientMergingParentImpl.java index 67ead2e8..35e80ceb 100644 --- a/processing/src/main/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorPatientMergingParentImpl.java +++ b/processing/src/main/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorPatientMergingParentImpl.java @@ -3,6 +3,7 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableList; import edu.harvard.hms.dbmi.avillach.hpds.data.genotype.InfoColumnMeta; import edu.harvard.hms.dbmi.avillach.hpds.data.genotype.VariantMasks; import edu.harvard.hms.dbmi.avillach.hpds.data.genotype.caching.VariantBucketHolder; @@ -32,12 +33,17 @@ public Set load(String conceptPath) { } }); - private List infoColumnsMeta; + private final List infoColumnsMeta; - private List patientIds; + private final List patientIds; + private final Set infoStoreColumns; public GenomicProcessorPatientMergingParentImpl(List nodes) { this.nodes = nodes; + + patientIds = initializePatientIds(); + infoStoreColumns = initializeInfoStoreColumns(); + infoColumnsMeta = initInfoColumnsMeta(); } @Override @@ -90,20 +96,23 @@ public Mono> getVariantList(DistributableQuery distributableQ @Override public List getPatientIds() { - if (patientIds != null) { - return patientIds; - } else { - // todo: verify all nodes have distinct patients - List result = Flux.just(nodes.toArray(GenomicProcessor[]::new)) - .flatMapSequential(node -> Mono.fromCallable(node::getPatientIds).subscribeOn(Schedulers.boundedElastic())) - .reduce((list1, list2) -> { - List concatenatedList = new ArrayList<>(list1); - concatenatedList.addAll(list2); - return concatenatedList; - }).block(); - patientIds = result; - return result; + return patientIds; + } + + private List initializePatientIds() { + List result = Flux.just(nodes.toArray(GenomicProcessor[]::new)) + .flatMapSequential(node -> Mono.fromCallable(node::getPatientIds).subscribeOn(Schedulers.boundedElastic())) + .reduce((list1, list2) -> { + List concatenatedList = new ArrayList<>(list1); + concatenatedList.addAll(list2); + return concatenatedList; + }).block(); + Set distinctPatientIds = new HashSet<>(result); + if (distinctPatientIds.size() != result.size()) { + log.warn((result.size() - distinctPatientIds.size()) + " duplicate patients found in patient partitions"); } + log.info(distinctPatientIds.size() + " patient ids loaded from patient partitions"); + return ImmutableList.copyOf(result); } @Override @@ -114,7 +123,10 @@ public Optional getMasks(String path, VariantBucketHolder getInfoStoreColumns() { - // todo: cache this + return infoStoreColumns; + } + + private Set initializeInfoStoreColumns() { return nodes.parallelStream() .map(GenomicProcessor::getInfoStoreColumns) .flatMap(Set::stream) @@ -128,10 +140,14 @@ public Set getInfoStoreValues(String conceptPath) { @Override public List getInfoColumnMeta() { - // todo: initialize on startup? - if (infoColumnsMeta == null) { - infoColumnsMeta = nodes.get(0).getInfoColumnMeta(); - } return infoColumnsMeta; } + + private List initInfoColumnsMeta() { + return nodes.parallelStream() + .map(GenomicProcessor::getInfoColumnMeta) + .map(HashSet::new) + .flatMap(Set::stream) + .collect(Collectors.toList()); + } } diff --git a/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/AbstractProcessorTest.java b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/AbstractProcessorTest.java index db3058ad..39cb32e9 100644 --- a/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/AbstractProcessorTest.java +++ b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/AbstractProcessorTest.java @@ -3,18 +3,18 @@ import edu.harvard.hms.dbmi.avillach.hpds.data.genotype.FileBackedByteIndexedInfoStore; import edu.harvard.hms.dbmi.avillach.hpds.data.query.Query; -import edu.harvard.hms.dbmi.avillach.hpds.storage.FileBackedByteIndexedStorage; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; +import java.math.BigInteger; import java.util.*; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) @@ -22,37 +22,18 @@ public class AbstractProcessorTest { private AbstractProcessor abstractProcessor; - private Map infoStores; - @Mock - private VariantService variantService; + private Map infoStores; @Mock private GenomicProcessor genomicProcessor; public static final String GENE_WITH_VARIANT_KEY = "Gene_with_variant"; - private static final String VARIANT_SEVERITY_KEY = "Variant_severity"; public static final List EXAMPLE_GENES_WITH_VARIANT = List.of("CDH8", "CDH9", "CDH10"); - public static final List EXAMPLE_VARIANT_SEVERITIES = List.of("HIGH", "MODERATE", "LOW"); @BeforeEach public void setup() { - FileBackedByteIndexedInfoStore mockInfoStore = mock(FileBackedByteIndexedInfoStore.class); - FileBackedByteIndexedStorage mockIndexedStorage = mock(FileBackedByteIndexedStorage.class); - when(mockIndexedStorage.keys()).thenReturn(new HashSet<>(EXAMPLE_GENES_WITH_VARIANT)); - when(mockInfoStore.getAllValues()).thenReturn(mockIndexedStorage); - - FileBackedByteIndexedInfoStore mockInfoStore2 = mock(FileBackedByteIndexedInfoStore.class); - FileBackedByteIndexedStorage mockIndexedStorage2 = mock(FileBackedByteIndexedStorage.class); - when(mockIndexedStorage2.keys()).thenReturn(new HashSet<>(EXAMPLE_VARIANT_SEVERITIES)); - when(mockInfoStore2.getAllValues()).thenReturn(mockIndexedStorage2); - - infoStores = Map.of( - GENE_WITH_VARIANT_KEY, mockInfoStore, - VARIANT_SEVERITY_KEY, mockInfoStore2 - ); - abstractProcessor = new AbstractProcessor( new PhenotypeMetaStore( new TreeMap<>(), @@ -77,54 +58,8 @@ public void getPatientSubsetForQuery_oneVariantCategoryFilter_indexFound() { Query.VariantInfoFilter variantInfoFilter = new Query.VariantInfoFilter(); variantInfoFilter.categoryVariantInfoFilters = categoryVariantInfoFilters; - List variantInfoFilters = List.of(variantInfoFilter); - - Query query = new Query(); - query.setVariantInfoFilters(variantInfoFilters); - - Set patientSubsetForQuery = abstractProcessor.getPatientSubsetForQuery(query); - assertFalse(patientSubsetForQuery.isEmpty()); - assertEquals(argumentCaptor.getValue(), new SparseVariantIndex(Set.of(2,4,6))); - } - - @Test - public void getPatientSubsetForQuery_oneVariantCategoryFilterTwoValues_unionFilters() { - //when(variantIndexCache.get(GENE_WITH_VARIANT_KEY, EXAMPLE_GENES_WITH_VARIANT.get(0))).thenReturn(new SparseVariantIndex(Set.of(2, 4))); - //when(variantIndexCache.get(GENE_WITH_VARIANT_KEY, EXAMPLE_GENES_WITH_VARIANT.get(1))).thenReturn(new SparseVariantIndex(Set.of(6))); - - ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(VariantIndex.class); - //when(patientVariantJoinHandler.getPatientIdsForIntersectionOfVariantSets(any(), argumentCaptor.capture())).thenReturn(List.of(Set.of(42))); - - Map categoryVariantInfoFilters = - Map.of(GENE_WITH_VARIANT_KEY, new String[] {EXAMPLE_GENES_WITH_VARIANT.get(0), EXAMPLE_GENES_WITH_VARIANT.get(1)}); - Query.VariantInfoFilter variantInfoFilter = new Query.VariantInfoFilter(); - variantInfoFilter.categoryVariantInfoFilters = categoryVariantInfoFilters; - - List variantInfoFilters = List.of(variantInfoFilter); - - Query query = new Query(); - query.setVariantInfoFilters(variantInfoFilters); - - Set patientSubsetForQuery = abstractProcessor.getPatientSubsetForQuery(query); - assertFalse(patientSubsetForQuery.isEmpty()); - // Expected result is the union of the two values - assertEquals(argumentCaptor.getValue(), new SparseVariantIndex(Set.of(2,4,6))); - } - - @Test - public void getPatientSubsetForQuery_twoVariantCategoryFilters_intersectFilters() { - //when(variantIndexCache.get(GENE_WITH_VARIANT_KEY, EXAMPLE_GENES_WITH_VARIANT.get(0))).thenReturn(new SparseVariantIndex(Set.of(2, 4, 6))); - //when(variantIndexCache.get(VARIANT_SEVERITY_KEY, EXAMPLE_VARIANT_SEVERITIES.get(0))).thenReturn(new SparseVariantIndex(Set.of(4, 5, 6, 7))); - - ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(VariantIndex.class); - //when(patientVariantJoinHandler.getPatientIdsForIntersectionOfVariantSets(any(), argumentCaptor.capture())).thenReturn(List.of(Set.of(42))); - - Map categoryVariantInfoFilters = Map.of( - GENE_WITH_VARIANT_KEY, new String[] {EXAMPLE_GENES_WITH_VARIANT.get(0)}, - VARIANT_SEVERITY_KEY, new String[] {EXAMPLE_VARIANT_SEVERITIES.get(0)} - ); - Query.VariantInfoFilter variantInfoFilter = new Query.VariantInfoFilter(); - variantInfoFilter.categoryVariantInfoFilters = categoryVariantInfoFilters; + when(genomicProcessor.getPatientMask(isA(DistributableQuery.class))).thenReturn(Mono.just(new BigInteger("1100110011"))); + when(genomicProcessor.patientMaskToPatientIdSet(eq(new BigInteger("1100110011")))).thenReturn(Set.of(42, 99)); List variantInfoFilters = List.of(variantInfoFilter); @@ -132,8 +67,6 @@ public void getPatientSubsetForQuery_twoVariantCategoryFilters_intersectFilters( query.setVariantInfoFilters(variantInfoFilters); Set patientSubsetForQuery = abstractProcessor.getPatientSubsetForQuery(query); - assertFalse(patientSubsetForQuery.isEmpty()); - // Expected result is the intersection of the two filters - assertEquals(argumentCaptor.getValue(), new SparseVariantIndex(Set.of(4, 6))); + assertEquals(Set.of(42, 99), patientSubsetForQuery); } } diff --git a/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/CountProcessorTest.java b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/CountProcessorTest.java index 030e9b46..6db908f1 100644 --- a/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/CountProcessorTest.java +++ b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/CountProcessorTest.java @@ -1,11 +1,11 @@ package edu.harvard.hms.dbmi.avillach.hpds.processing; import edu.harvard.hms.dbmi.avillach.hpds.data.query.Query; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.test.context.event.annotation.BeforeTestClass; import java.io.IOException; import java.util.ArrayList; @@ -18,14 +18,13 @@ @ExtendWith(MockitoExtension.class) public class CountProcessorTest { - private CountProcessor countProcessor; + private final CountProcessor countProcessor; - @Mock - private AbstractProcessor mockAbstractProcessor; + private final AbstractProcessor mockAbstractProcessor; - @BeforeTestClass - public void before() { - countProcessor = new CountProcessor(mockAbstractProcessor); + public CountProcessorTest(@Mock AbstractProcessor mockAbstractProcessor) { + this.mockAbstractProcessor = mockAbstractProcessor; + this.countProcessor = new CountProcessor(mockAbstractProcessor); } @Test diff --git a/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorParentImplTest.java b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorParentImplTest.java new file mode 100644 index 00000000..0ab4a743 --- /dev/null +++ b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorParentImplTest.java @@ -0,0 +1,64 @@ +package edu.harvard.hms.dbmi.avillach.hpds.processing; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; + +import java.math.BigInteger; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class GenomicProcessorParentImplTest { + @Mock + private GenomicProcessor mockProcessor1; + @Mock + private GenomicProcessor mockProcessor2; + @Mock + private GenomicProcessor mockProcessor3; + + private GenomicProcessorParentImpl parentProcessor; + + @Test + public void patientIdInit_patientsMatch_noException() { + when(mockProcessor1.getPatientIds()).thenReturn(List.of("1", "42", "99")); + when(mockProcessor2.getPatientIds()).thenReturn(List.of("1", "42", "99")); + when(mockProcessor3.getPatientIds()).thenReturn(List.of("1", "42", "99")); + parentProcessor = new GenomicProcessorParentImpl(List.of( + mockProcessor1, mockProcessor2, mockProcessor3 + )); + } + @Test + public void patientIdInit_patientsDiffer_exception() { + when(mockProcessor1.getPatientIds()).thenReturn(List.of("1", "42", "99")); + when(mockProcessor2.getPatientIds()).thenReturn(List.of("1", "43", "99")); + when(mockProcessor3.getPatientIds()).thenReturn(List.of("1", "42", "99")); + + assertThrows(IllegalStateException.class, () -> { + parentProcessor = new GenomicProcessorParentImpl(List.of( + mockProcessor1, mockProcessor2, mockProcessor3 + )); + }); + } + + + @Test + public void getPatientMask_validResponses_returnMerged() { + DistributableQuery distributableQuery = new DistributableQuery(); + when(mockProcessor1.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("110110000011", 2))); + when(mockProcessor2.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("110001100011", 2))); + when(mockProcessor3.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("110000000111", 2))); + parentProcessor = new GenomicProcessorParentImpl(List.of( + mockProcessor1, mockProcessor2, mockProcessor3 + )); + + BigInteger patientMask = parentProcessor.getPatientMask(distributableQuery).block(); + BigInteger expectedPatientMask = new BigInteger("110111100111", 2); + assertEquals(expectedPatientMask, patientMask); + } +} \ No newline at end of file diff --git a/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorPatientMergingParentImplTest.java b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorPatientMergingParentImplTest.java new file mode 100644 index 00000000..06fe6f81 --- /dev/null +++ b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/GenomicProcessorPatientMergingParentImplTest.java @@ -0,0 +1,106 @@ +package edu.harvard.hms.dbmi.avillach.hpds.processing; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.boot.test.system.CapturedOutput; +import org.springframework.boot.test.system.OutputCaptureExtension; +import reactor.core.publisher.Mono; + +import java.math.BigInteger; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.when; + +@ExtendWith({MockitoExtension.class, OutputCaptureExtension.class}) +public class GenomicProcessorPatientMergingParentImplTest { + + @Mock + private GenomicProcessor mockProcessor1; + @Mock + private GenomicProcessor mockProcessor2; + @Mock + private GenomicProcessor mockProcessor3; + + private GenomicProcessorPatientMergingParentImpl patientMergingParent; + + @BeforeEach + public void setup() { + patientMergingParent = new GenomicProcessorPatientMergingParentImpl(List.of( + mockProcessor1, mockProcessor2, mockProcessor3 + )); + } + + @Test + public void getPatientMask_validResponses_returnMerged() { + DistributableQuery distributableQuery = new DistributableQuery(); + when(mockProcessor1.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("11011011", 2))); + when(mockProcessor2.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("110001100011", 2))); + when(mockProcessor3.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("11000111", 2))); + BigInteger patientMask = patientMergingParent.getPatientMask(distributableQuery).block(); + BigInteger expectedPatientMask = new BigInteger("11011000011000000111", 2); + assertEquals(expectedPatientMask, patientMask); + } + + @Test + public void getPatientMask_noPatientResponses_returnMerged() { + DistributableQuery distributableQuery = new DistributableQuery(); + when(mockProcessor1.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("11011011", 2))); + when(mockProcessor2.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("110000000011", 2))); + when(mockProcessor3.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("11000011", 2))); + BigInteger patientMask = patientMergingParent.getPatientMask(distributableQuery).block(); + BigInteger expectedPatientMask = new BigInteger("11011000000000000011", 2); + assertEquals(expectedPatientMask, patientMask); + } + + @Test + public void getPatientMask_emptyResponses_returnMerged() { + DistributableQuery distributableQuery = new DistributableQuery(); + when(mockProcessor1.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("11011011", 2))); + when(mockProcessor2.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("1111", 2))); + when(mockProcessor3.getPatientMask(distributableQuery)).thenReturn(Mono.just(new BigInteger("11000111", 2))); + BigInteger patientMask = patientMergingParent.getPatientMask(distributableQuery).block(); + BigInteger expectedPatientMask = new BigInteger("110110000111", 2); + assertEquals(expectedPatientMask, patientMask); + } + + @Test + public void patientIdInit_validPatients_noException(CapturedOutput output) { + when(mockProcessor1.getPatientIds()).thenReturn(List.of("1", "42", "99")); + when(mockProcessor2.getPatientIds()).thenReturn(List.of("2", "50", "100")); + when(mockProcessor3.getPatientIds()).thenReturn(List.of("1000", "10001", "1002", "1003")); + + patientMergingParent = new GenomicProcessorPatientMergingParentImpl(List.of( + mockProcessor1, mockProcessor2, mockProcessor3 + )); + + assertFalse(output.getOut().contains("duplicate patients found in patient partitions")); + } + @Test + public void patientIdInit_invalidPatients_warnMessage(CapturedOutput output) { + when(mockProcessor1.getPatientIds()).thenReturn(List.of("1", "42", "99")); + when(mockProcessor2.getPatientIds()).thenReturn(List.of("2", "42", "100")); + when(mockProcessor3.getPatientIds()).thenReturn(List.of("1000", "10001", "1002", "1003")); + + patientMergingParent = new GenomicProcessorPatientMergingParentImpl(List.of( + mockProcessor1, mockProcessor2, mockProcessor3 + )); + + assertTrue(output.getOut().contains("1 duplicate patients found in patient partitions")); + } + @Test + public void patientIdInit_multipleInvalidPatients_warnMessage(CapturedOutput output) { + when(mockProcessor1.getPatientIds()).thenReturn(List.of("1", "42", "99")); + when(mockProcessor2.getPatientIds()).thenReturn(List.of("2", "50", "100")); + when(mockProcessor3.getPatientIds()).thenReturn(List.of("1", "42", "99", "1003")); + + patientMergingParent = new GenomicProcessorPatientMergingParentImpl(List.of( + mockProcessor1, mockProcessor2, mockProcessor3 + )); + + assertTrue(output.getOut().contains("3 duplicate patients found in patient partitions")); + } +} \ No newline at end of file diff --git a/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/PatientVariantJoinHandlerTest.java b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/PatientVariantJoinHandlerTest.java index 9abf035e..c2d5f252 100644 --- a/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/PatientVariantJoinHandlerTest.java +++ b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/PatientVariantJoinHandlerTest.java @@ -19,7 +19,6 @@ @ExtendWith(MockitoExtension.class) public class PatientVariantJoinHandlerTest { - @Mock private VariantService variantService; private PatientVariantJoinHandler patientVariantJoinHandler; @@ -28,8 +27,8 @@ public class PatientVariantJoinHandlerTest { public static final Set PATIENT_IDS_INTEGERS = Set.of(PATIENT_IDS).stream().map(Integer::parseInt).collect(Collectors.toSet()); public static final String[] VARIANT_INDEX = {"16,61642243,A,T", "16,61642252,A,G", "16,61642256,C,T", "16,61642257,G,A", "16,61642258,G,A", "16,61642259,G,A", "16,61642260,G,A", "16,61642261,G,A"}; - @BeforeTestClass - public void setUp() { + public PatientVariantJoinHandlerTest(@Mock VariantService variantService) { + this.variantService = variantService; patientVariantJoinHandler = new PatientVariantJoinHandler(variantService); when(variantService.getVariantIndex()).thenReturn(VARIANT_INDEX); } @@ -51,7 +50,7 @@ public void getPatientIdsForIntersectionOfVariantSets_allPatientsMatchOneVariant when(variantService.getMasks(eq(VARIANT_INDEX[2]), any())).thenReturn(Optional.of(emptyVariantMasks)); when(variantService.getMasks(eq(VARIANT_INDEX[4]), any())).thenReturn(Optional.of(emptyVariantMasks)); - Set patientIdsForIntersectionOfVariantSets = patientMaskToPatientIdSet(patientVariantJoinHandler.getPatientIdsForIntersectionOfVariantSets(Set.of(), intersectionOfInfoFilters)); + Set patientIdsForIntersectionOfVariantSets = patientMaskToPatientIdSet(patientVariantJoinHandler.getPatientIdsForIntersectionOfVariantSets(null, intersectionOfInfoFilters)); // this should be all patients, as all patients match one of the variants assertEquals(PATIENT_IDS_INTEGERS, patientIdsForIntersectionOfVariantSets); } @@ -73,7 +72,7 @@ public void getPatientIdsForIntersectionOfVariantSets_allPatientsMatchOneVariant when(variantService.getMasks(eq(VARIANT_INDEX[2]), any())).thenReturn(Optional.empty()); when(variantService.getMasks(eq(VARIANT_INDEX[4]), any())).thenReturn(Optional.empty()); - Set patientIdsForIntersectionOfVariantSets = patientMaskToPatientIdSet(patientVariantJoinHandler.getPatientIdsForIntersectionOfVariantSets(Set.of(), intersectionOfInfoFilters)); + Set patientIdsForIntersectionOfVariantSets = patientMaskToPatientIdSet(patientVariantJoinHandler.getPatientIdsForIntersectionOfVariantSets(null, intersectionOfInfoFilters)); // this should be all patients, as all patients match one of the variants assertEquals(PATIENT_IDS_INTEGERS, patientIdsForIntersectionOfVariantSets); } @@ -112,7 +111,7 @@ public void getPatientIdsForIntersectionOfVariantSets_somePatientsMatchVariants( when(variantService.getMasks(eq(VARIANT_INDEX[0]), any())).thenReturn(Optional.of(variantMasks)); when(variantService.getMasks(eq(VARIANT_INDEX[2]), any())).thenReturn(Optional.of(variantMasks2)); - Set patientIdsForIntersectionOfVariantSets = patientMaskToPatientIdSet(patientVariantJoinHandler.getPatientIdsForIntersectionOfVariantSets(Set.of(), intersectionOfInfoFilters)); + Set patientIdsForIntersectionOfVariantSets = patientMaskToPatientIdSet(patientVariantJoinHandler.getPatientIdsForIntersectionOfVariantSets(null, intersectionOfInfoFilters)); // this should be all patients who match at least one variant assertEquals(Set.of(101, 103, 105), patientIdsForIntersectionOfVariantSets); } @@ -120,8 +119,9 @@ public void getPatientIdsForIntersectionOfVariantSets_somePatientsMatchVariants( @Test public void getPatientIdsForIntersectionOfVariantSets_noVariants() { VariantIndex intersectionOfInfoFilters = VariantIndex.empty(); + when(variantService.getPatientIds()).thenReturn(PATIENT_IDS); - Set patientIdsForIntersectionOfVariantSets = patientMaskToPatientIdSet(patientVariantJoinHandler.getPatientIdsForIntersectionOfVariantSets(Set.of(), intersectionOfInfoFilters)); + Set patientIdsForIntersectionOfVariantSets = patientMaskToPatientIdSet(patientVariantJoinHandler.getPatientIdsForIntersectionOfVariantSets(null, intersectionOfInfoFilters)); // this should be empty, as there are no variants assertEquals(Set.of(), patientIdsForIntersectionOfVariantSets); } diff --git a/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/genomic/GenomicProcessorRestClientTest.java b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/genomic/GenomicProcessorRestClientTest.java index c5753f83..9381ac76 100644 --- a/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/genomic/GenomicProcessorRestClientTest.java +++ b/processing/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/processing/genomic/GenomicProcessorRestClientTest.java @@ -3,6 +3,7 @@ import edu.harvard.hms.dbmi.avillach.hpds.data.genotype.InfoColumnMeta; import edu.harvard.hms.dbmi.avillach.hpds.data.query.Query; import edu.harvard.hms.dbmi.avillach.hpds.processing.DistributableQuery; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -14,6 +15,7 @@ import java.util.stream.Collectors; import java.util.stream.IntStream; +@Disabled public class GenomicProcessorRestClientTest { private GenomicProcessorRestClient genomicProcessorRestClient = new GenomicProcessorRestClient("http://localhost:8090/"); diff --git a/service/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/service/util/AbstractProcessorIntegrationTest.java b/service/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/service/util/AbstractProcessorIntegrationTest.java index bd771cda..a814ed42 100644 --- a/service/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/service/util/AbstractProcessorIntegrationTest.java +++ b/service/src/test/java/edu/harvard/hms/dbmi/avillach/hpds/service/util/AbstractProcessorIntegrationTest.java @@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +@Disabled @ExtendWith(SpringExtension.class) @EnableAutoConfiguration @SpringBootTest(classes = HpdsApplication.class)