diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/5100-fixed-bug-in-mdm-matching.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/5100-fixed-bug-in-mdm-matching.yaml new file mode 100644 index 00000000000..0bedea61f69 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/6_8_0/5100-fixed-bug-in-mdm-matching.yaml @@ -0,0 +1,7 @@ +--- +type: fix +issue: 5100 +title: "Fixed a bug in FindCandidateByExampleSvc that resulted in + class cast exceptions for IResourcePersistentIds that are not + based on Long value ids. +" diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java index 802b600b960..88ca77a75ee 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java @@ -93,7 +93,7 @@ public class FindCandidateByExampleSvc

extends // we'll track the added ids so we don't add the same resources twice // note, all these resources are the same type, so we only need the Long value - Set currentIds = new HashSet<>(); + Set currentIds = new HashSet<>(); for (MatchedTarget match : matchedCandidates) { Optional optionalMdmLink = myMdmLinkDaoSvc.getMatchedLinkForSourcePid( myIdHelperService.getPidOrNull(RequestPartitionId.allPartitions(), match.getTarget())); @@ -125,7 +125,11 @@ public class FindCandidateByExampleSvc

extends } // only add if it's not already in the list - if (currentIds.add((Long) candidate.getCandidateGoldenResourcePid().getId())) { + // NB: we cannot use hash of IResourcePersistentId because + // BaseResourcePersistentId overrides this (and so is the same + // for any class with the same version) :( + if (currentIds.add( + String.valueOf(candidate.getCandidateGoldenResourcePid().getId()))) { retval.add(candidate); } } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/testmodels/StringResourceId.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/testmodels/StringResourceId.java new file mode 100644 index 00000000000..e5a40617b42 --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/testmodels/StringResourceId.java @@ -0,0 +1,26 @@ +package ca.uhn.fhir.jpa.mdm.helper.testmodels; + +import ca.uhn.fhir.rest.api.server.storage.BaseResourcePersistentId; + +/** + * A test ResourceId with a non-long implementation of the id + */ +public class StringResourceId extends BaseResourcePersistentId { + + private final String myId; + + public StringResourceId(String theId) { + super(null); + myId = theId; + } + + public StringResourceId(Long theVersion, String theId) { + super(theVersion, null); + myId = theId; + } + + @Override + public String getId() { + return myId; + } +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/testmodels/TestMdmLink.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/testmodels/TestMdmLink.java new file mode 100644 index 00000000000..a2432a5308b --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/testmodels/TestMdmLink.java @@ -0,0 +1,184 @@ +package ca.uhn.fhir.jpa.mdm.helper.testmodels; + +import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; +import ca.uhn.fhir.mdm.api.IMdmLink; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; + +import java.util.Date; + +public class TestMdmLink implements IMdmLink { + + private StringResourceId myId; + + private StringResourceId myGoldenResourceId; + + private StringResourceId mySourceResourceId; + + private MdmMatchResultEnum myMatchResultEnum; + + private MdmLinkSourceEnum myLinkSourceEnum; + + @Override + public StringResourceId getId() { + return myId; + } + + @Override + public IMdmLink setId(StringResourceId theId) { + myId = theId; + return this; + } + + @Override + public StringResourceId getGoldenResourcePersistenceId() { + return myGoldenResourceId; + } + + @Override + public IMdmLink setGoldenResourcePersistenceId(StringResourceId theGoldenResourcePid) { + myGoldenResourceId = theGoldenResourcePid; + return this; + } + + @Override + public StringResourceId getSourcePersistenceId() { + return mySourceResourceId; + } + + @Override + public IMdmLink setSourcePersistenceId(StringResourceId theSourcePid) { + mySourceResourceId = theSourcePid; + return this; + } + + @Override + public MdmMatchResultEnum getMatchResult() { + return myMatchResultEnum; + } + + @Override + public IMdmLink setMatchResult(MdmMatchResultEnum theMatchResult) { + myMatchResultEnum = theMatchResult; + return this; + } + + @Override + public MdmLinkSourceEnum getLinkSource() { + return myLinkSourceEnum; + } + + @Override + public IMdmLink setLinkSource(MdmLinkSourceEnum theLinkSource) { + myLinkSourceEnum = theLinkSource; + return this; + } + + @Override + public Date getCreated() { + return new Date(); + } + + @Override + public IMdmLink setCreated(Date theCreated) { + // unneeded + return this; + } + + @Override + public Date getUpdated() { + return new Date(); + } + + @Override + public IMdmLink setUpdated(Date theUpdated) { + // unneeded + return this; + } + + @Override + public String getVersion() { + return null; + } + + @Override + public IMdmLink setVersion(String theVersion) { + // unneeded + return this; + } + + @Override + public Boolean getEidMatch() { + return null; + } + + @Override + public Boolean isEidMatchPresent() { + return null; + } + + @Override + public IMdmLink setEidMatch(Boolean theEidMatch) { + return this; + } + + @Override + public Boolean getHadToCreateNewGoldenResource() { + return null; + } + + @Override + public IMdmLink setHadToCreateNewGoldenResource(Boolean theHadToCreateNewGoldenResource) { + return this; + } + + @Override + public Long getVector() { + return null; + } + + @Override + public IMdmLink setVector(Long theVector) { + return this; + } + + @Override + public Double getScore() { + return null; + } + + @Override + public IMdmLink setScore(Double theScore) { + return this; + } + + @Override + public Long getRuleCount() { + return null; + } + + @Override + public IMdmLink setRuleCount(Long theRuleCount) { + return this; + } + + @Override + public String getMdmSourceType() { + return null; + } + + @Override + public IMdmLink setMdmSourceType(String theMdmSourceType) { + return this; + } + + @Override + public void setPartitionId(PartitionablePartitionId thePartitionablePartitionId) { + + } + + @Override + public PartitionablePartitionId getPartitionId() { + return null; + } +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvcTest.java new file mode 100644 index 00000000000..0b63d4c916f --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvcTest.java @@ -0,0 +1,139 @@ +package ca.uhn.fhir.jpa.mdm.svc.candidate; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.svc.IIdHelperService; +import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; +import ca.uhn.fhir.jpa.mdm.helper.testmodels.StringResourceId; +import ca.uhn.fhir.jpa.mdm.helper.testmodels.TestMdmLink; +import ca.uhn.fhir.mdm.api.IMdmLink; +import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc; +import ca.uhn.fhir.mdm.api.MatchedTarget; +import ca.uhn.fhir.mdm.api.MdmMatchOutcome; +import ca.uhn.fhir.mdm.util.MdmPartitionHelper; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class FindCandidateByExampleSvcTest { + + @Spy + private FhirContext myFhirContext = FhirContext.forR4Cached(); + + @Mock + private IIdHelperService myIdHelperService; + + @Mock + private MdmLinkDaoSvc> myMdmLinkDaoSvc; + + @Mock + private MdmPartitionHelper myMdmPartitionHelper; + + @Mock + private IMdmMatchFinderSvc myMdmMatchFinderSvc; + + @InjectMocks + private FindCandidateByExampleSvc myFindCandidateByExampleSvc; + + @Test + public void findMatchGoldenResourceCandidates_withNonLongIds_returnsList() { + // setup + int total = 3; + Patient patient = new Patient(); + patient.setActive(true); + patient.addName() + .setFamily("Simpson") + .addGiven("Homer"); + + List matchCandidatesList = new ArrayList<>(); + Map targets = new HashMap<>(); + for (int i = 0; i < total*2; i++) { + String id = "Test-" + (i % 3); + if (!targets.containsKey(id)) { + // the different names don't matter for the test + // but makes debugging easier + String name; + if (i == 0) { + name = "Bart"; + } else if (i == 1) { + name = "Lisa"; + } else { + name = "Maggie"; + } + Patient p = new Patient(); + p.setActive(true); + p.addName() + .setFamily("Simpson") + .addGiven(name); + + targets.put(id, p); + } + + matchCandidatesList.add( + new MatchedTarget(targets.get(id), MdmMatchOutcome.POSSIBLE_MATCH) + ); + } + + // when + when(myMdmPartitionHelper.getRequestPartitionIdFromResourceForSearch(any())) + .thenReturn(RequestPartitionId.allPartitions()); + when(myIdHelperService.getPidOrNull(any(), any())) + .thenReturn(new StringResourceId("omit")); + when(myMdmLinkDaoSvc.getMdmLinksBySourcePidAndMatchResult(any(), any())) + .thenReturn(new ArrayList<>()); + when(myMdmMatchFinderSvc.getMatchedTargets(anyString(), any(Patient.class), any())) + .thenReturn(matchCandidatesList); + // we don't care about the id we return here + // because we are going to mock the return value anyways + when(myIdHelperService.getPidOrNull(any(), any(Patient.class))) + .thenReturn(new StringResourceId("test")); + int[] count = new int[] { 0 }; + when(myMdmLinkDaoSvc.getMatchedLinkForSourcePid(any(StringResourceId.class))) + .thenAnswer(args -> { + if (count[0] < total) { + TestMdmLink link = new TestMdmLink(); + link.setSourcePersistenceId(args.getArgument(0)); + if (count[0] % 2 == 0) { + link.setGoldenResourcePersistenceId(new StringResourceId("even")); + } else { + link.setGoldenResourcePersistenceId(new StringResourceId("odd")); + } + count[0]++; + return Optional.of(link); + } + return Optional.empty(); + }); + + // test + List goldenResourceCanddiates = myFindCandidateByExampleSvc.findMatchGoldenResourceCandidates(patient); + + // verify + assertNotNull(goldenResourceCanddiates); + assertEquals(2, goldenResourceCanddiates.size()); + Set ids = new HashSet<>(); + for (MatchedGoldenResourceCandidate r : goldenResourceCanddiates) { + // we know these are strings + assertTrue(ids.add((String)r.getCandidateGoldenResourcePid().getId())); + } + } +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvcIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvcIT.java new file mode 100644 index 00000000000..5b4cf0180fe --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvcIT.java @@ -0,0 +1,157 @@ +package ca.uhn.fhir.jpa.mdm.svc.candidate; + +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; +import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.TestPropertySource; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +@ExtendWith(MockitoExtension.class) +@TestPropertySource(properties = { + "module.mdm.config.script.file=classpath:mdm/mdm-rules-john-doe.json" +}) +class MdmGoldenResourceFindingSvcIT extends BaseMdmR4Test { + + @Autowired + MdmGoldenResourceFindingSvc myMdmGoldenResourceFindingSvc = new MdmGoldenResourceFindingSvc(); + @Autowired + MdmLinkDaoSvc myMdmLinkDaoSvc; + + @Test + public void testNoMatchCandidatesSkipped() { + // setup + Patient jane = createPatientAndUpdateLinks(addExternalEID(buildJanePatient(), EID_1)); + + // hack the link into a NO_MATCH + List links = (List) myMdmLinkDaoSvc.findMdmLinksBySourceResource(jane); + assertThat(links, hasSize(1)); + MdmLink link = links.get(0); + link.setMatchResult(MdmMatchResultEnum.NO_MATCH); + link.setLinkSource(MdmLinkSourceEnum.MANUAL); + myMdmLinkDaoSvc.save(link); + + // the NO_MATCH golden resource should not be a candidate + CandidateList candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(jane); + assertEquals(0, candidateList.size()); + } + + @Test + public void findMdmLinksBySourceResource_withMatchingResources_doesNotReturnDuplicates() { + // setup + // create a bunch of patients that match + // (according to the rules in mdm-rules-john-doe.json) + // patient 1 + { + String patientStr = """ + { + "resourceType": "Patient", + "name": [ { + "family": "Jho", + "given": [ "Doe"] + } ], + "birthDate": "1974-12-25" + } + """; + createPatientFromJsonString(patientStr, true); + } + // patient 2 + { + String patientStr = """ + { + "resourceType": "Patient", + "name": [ { + "family": "Jhyee", + "given": [ "Deeon"] + } ], + "birthDate": "1974-12-25" + } + """; + createPatientFromJsonString(patientStr, true); + } + // patient 3 + { + String patientStr = """ + { + "resourceType": "Patient", + "name": [ { + "family": "Jhoye", + "given": [ "Deo"] + } ], + "birthDate": "1974-12-25" + } + """; + createPatientFromJsonString(patientStr, true); + } + // patient 4 + { + String patientStr = """ + { + "resourceType": "Patient", + "name": [ { + "family": "Jhee", + "given": [ "Deo"] + } ], + "birthDate": "1974-12-25" + } + """; + createPatientFromJsonString(patientStr, true); + } + // patient 5 + Patient candidate; + { + String patientStr = """ + { + "resourceType": "Patient", + "name": [ { + "family": "Jhee", + "given": [ "Doe"] + } ], + "birthDate": "1974-12-25" + } + """; + candidate = createPatientFromJsonString(patientStr, true); + } + + // test + CandidateList candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(candidate); + + // verify + assertNotNull(candidateList); + Set ids = new HashSet<>(); + for (MatchedGoldenResourceCandidate c : candidateList.getCandidates()) { + assertTrue(ids.add((Long) c.getCandidateGoldenResourcePid().getId())); + } + } + + private Patient createPatientFromJsonString(String theStr, boolean theCreateGolden) { + Patient patient = (Patient) myFhirContext.newJsonParser().parseResource(theStr); + DaoMethodOutcome daoOutcome = myPatientDao.create( + patient, + new SystemRequestDetails() + ); + + if (theCreateGolden) { + myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient, createContextForCreate("Patient")); + } + + return (Patient) daoOutcome.getResource(); + } +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvcTest.java deleted file mode 100644 index 0cd440e92b2..00000000000 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmGoldenResourceFindingSvcTest.java +++ /dev/null @@ -1,163 +0,0 @@ -package ca.uhn.fhir.jpa.mdm.svc.candidate; - -import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; -import ca.uhn.fhir.jpa.entity.MdmLink; -import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; -import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; -import ca.uhn.fhir.mdm.api.IMdmSettings; -import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; -import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; -import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; -import ca.uhn.fhir.rest.api.server.SystemRequestDetails; -import ca.uhn.fhir.test.utilities.UnregisterScheduledProcessor; -import org.hl7.fhir.r4.model.Patient; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; - -import java.io.IOException; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@ExtendWith(MockitoExtension.class) -@TestPropertySource(properties = { - "module.mdm.config.script.file=classpath:mdm/mdm-rules-john-doe.json" -}) -class MdmGoldenResourceFindingSvcTest extends BaseMdmR4Test { - - @Autowired - MdmGoldenResourceFindingSvc myMdmGoldenResourceFindingSvc = new MdmGoldenResourceFindingSvc(); - @Autowired - MdmLinkDaoSvc myMdmLinkDaoSvc; - - @Test - public void testNoMatchCandidatesSkipped() { - // setup - Patient jane = createPatientAndUpdateLinks(addExternalEID(buildJanePatient(), EID_1)); - - // hack the link into a NO_MATCH - List links = (List) myMdmLinkDaoSvc.findMdmLinksBySourceResource(jane); - assertThat(links, hasSize(1)); - MdmLink link = links.get(0); - link.setMatchResult(MdmMatchResultEnum.NO_MATCH); - link.setLinkSource(MdmLinkSourceEnum.MANUAL); - myMdmLinkDaoSvc.save(link); - - // the NO_MATCH golden resource should not be a candidate - CandidateList candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(jane); - assertEquals(0, candidateList.size()); - } - - @Test - public void findMdmLinksBySourceResource_withMatchingResources_doesNotReturnDuplicates() throws IOException { - // setup - // create a bunch of patients that match - // (according to the rules in mdm-rules-john-doe.json) - // patient 1 - { - String patientStr = """ - { - "resourceType": "Patient", - "name": [ { - "family": "Jho", - "given": [ "Doe"] - } ], - "birthDate": "1974-12-25" - } - """; - createPatientFromJsonString(patientStr, true); - } - // patient 2 - { - String patientStr = """ - { - "resourceType": "Patient", - "name": [ { - "family": "Jhyee", - "given": [ "Deeon"] - } ], - "birthDate": "1974-12-25" - } - """; - createPatientFromJsonString(patientStr, true); - } - // patient 3 - { - String patientStr = """ - { - "resourceType": "Patient", - "name": [ { - "family": "Jhoye", - "given": [ "Deo"] - } ], - "birthDate": "1974-12-25" - } - """; - createPatientFromJsonString(patientStr, true); - } - // patient 4 - { - String patientStr = """ - { - "resourceType": "Patient", - "name": [ { - "family": "Jhee", - "given": [ "Deo"] - } ], - "birthDate": "1974-12-25" - } - """; - createPatientFromJsonString(patientStr, true); - } - // patient 5 - Patient candidate; - { - String patientStr = """ - { - "resourceType": "Patient", - "name": [ { - "family": "Jhee", - "given": [ "Doe"] - } ], - "birthDate": "1974-12-25" - } - """; - candidate = createPatientFromJsonString(patientStr, true); - } - - // test - CandidateList candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(candidate); - - // verify - assertNotNull(candidateList); - Set ids = new HashSet<>(); - for (MatchedGoldenResourceCandidate c : candidateList.getCandidates()) { - assertTrue(ids.add((Long)c.getCandidateGoldenResourcePid().getId())); - } - } - - private Patient createPatientFromJsonString(String theStr, boolean theCreateGolden) { - Patient patient = (Patient) myFhirContext.newJsonParser().parseResource(theStr); - DaoMethodOutcome daoOutcome = myPatientDao.create(patient, new SystemRequestDetails()); - - if (theCreateGolden) { - myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient, createContextForCreate("Patient")); - } - - return (Patient) daoOutcome.getResource(); - } - -} diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/concurrency/PointcutLatchSession.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/concurrency/PointcutLatchSession.java index 971645e7fc6..a0bd207b57a 100644 --- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/concurrency/PointcutLatchSession.java +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/test/concurrency/PointcutLatchSession.java @@ -66,6 +66,9 @@ public class PointcutLatchSession { List awaitExpectedWithTimeout(int theTimeoutSecond) throws InterruptedException { if (!myCountdownLatch.await(theTimeoutSecond, TimeUnit.SECONDS)) { + if (!myFailures.isEmpty()) { + ourLog.error(String.join(",", myFailures)); + } throw new LatchTimedOutError(Msg.code(1483) + myName + " timed out waiting " + theTimeoutSecond + " seconds for latch to countdown from " + myInitialCount + " to 0. Is " + myCountdownLatch.getCount() + "."); }