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 extends IMdmLink> 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() + ".");
}