package ca.uhn.fhir.jpa.term; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import java.util.List; import java.util.Set; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.AfterClass; import org.junit.Test; import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.TestUtil; public class TerminologySvcImplTest extends BaseJpaDstu3Test { private static final String CS_URL = "http://example.com/my_code_system"; @AfterClass public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); } @Test public void testStoreCodeSystemInvalidCyclicLoop() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); TermCodeSystemVersion cs = new TermCodeSystemVersion(); cs.setResource(table); cs.setResourceVersionId(table.getVersion()); TermConcept parent = new TermConcept(); parent.setCodeSystem(cs); parent.setCode("parent"); cs.getConcepts().add(parent); TermConcept child = new TermConcept(); child.setCodeSystem(cs); child.setCode("child"); parent.addChild(child, RelationshipTypeEnum.ISA); child.addChild(parent, RelationshipTypeEnum.ISA); try { myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", cs); fail(); } catch (InvalidRequestException e) { assertEquals("CodeSystem contains circular reference around code parent", e.getMessage()); } } @Test public void testFindCodesAboveAndBelowUnknown() { createCodeSystem(); assertThat(myTermSvc.findCodesBelow("http://foo", "code"), empty()); assertThat(myTermSvc.findCodesBelow(CS_URL, "code"), empty()); assertThat(myTermSvc.findCodesAbove("http://foo", "code"), empty()); assertThat(myTermSvc.findCodesAbove(CS_URL, "code"), empty()); } @Test public void testFindCodesBelowA() { IIdType id = createCodeSystem(); Set<TermConcept> concepts; Set<String> codes; concepts = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "ParentA"); codes = toCodes(concepts); assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA", "childAAB", "childAB")); concepts = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "childAA"); codes = toCodes(concepts); assertThat(codes, containsInAnyOrder("childAA", "childAAA", "childAAB")); // Try an unknown code concepts = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "FOO_BAD_CODE"); codes = toCodes(concepts); assertThat(codes, empty()); } @Test public void testFindCodesBelowBuiltInCodeSystem() { List<VersionIndependentConcept> concepts; Set<String> codes; concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status", "inactive"); codes = toCodes(concepts); assertThat(codes, containsInAnyOrder("inactive", "resolved")); concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status", "resolved"); codes = toCodes(concepts); assertThat(codes, containsInAnyOrder("resolved")); // Unknown code concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status", "FOO"); codes = toCodes(concepts); assertThat(codes, empty()); // Unknown system concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status2222", "active"); codes = toCodes(concepts); assertThat(codes, empty()); } @Test public void testFindCodesAboveBuiltInCodeSystem() { List<VersionIndependentConcept> concepts; Set<String> codes; concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status", "active"); codes = toCodes(concepts); assertThat(codes, containsInAnyOrder("active")); concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status", "resolved"); codes = toCodes(concepts); assertThat(codes, containsInAnyOrder("inactive", "resolved")); // Unknown code concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status", "FOO"); codes = toCodes(concepts); assertThat(codes, empty()); // Unknown system concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status2222", "active"); codes = toCodes(concepts); assertThat(codes, empty()); } @Test public void testReindexTerminology() { IIdType id = createCodeSystem(); assertThat(mySystemDao.markAllResourcesForReindexing(), greaterThan(0)); assertThat(mySystemDao.performReindexingPass(100), greaterThan(0)); } private IIdType createCodeSystem() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); TermCodeSystemVersion cs = new TermCodeSystemVersion(); cs.setResource(table); cs.setResourceVersionId(table.getVersion()); TermConcept parentA = new TermConcept(cs, "ParentA"); cs.getConcepts().add(parentA); TermConcept childAA = new TermConcept(cs, "childAA"); parentA.addChild(childAA, RelationshipTypeEnum.ISA); TermConcept childAAA = new TermConcept(cs, "childAAA"); childAA.addChild(childAAA, RelationshipTypeEnum.ISA); TermConcept childAAB = new TermConcept(cs, "childAAB"); childAA.addChild(childAAB, RelationshipTypeEnum.ISA); TermConcept childAB = new TermConcept(cs, "childAB"); parentA.addChild(childAB, RelationshipTypeEnum.ISA); TermConcept parentB = new TermConcept(cs, "ParentB"); cs.getConcepts().add(parentB); myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", cs); return id; } @Test public void testFindCodesAbove() { IIdType id = createCodeSystem(); Set<TermConcept> concepts; Set<String> codes; concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "childAA"); codes = toCodes(concepts); assertThat(codes, containsInAnyOrder("ParentA", "childAA")); concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "childAAB"); codes = toCodes(concepts); assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAB")); // Try an unknown code concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "FOO_BAD_CODE"); codes = toCodes(concepts); assertThat(codes, empty()); } @Test public void testCreateDuplicateCodeSystemUri() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); TermCodeSystemVersion cs = new TermCodeSystemVersion(); cs.setResource(table); cs.setResourceVersionId(table.getVersion()); myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, cs); // Update cs = new TermCodeSystemVersion(); TermConcept parentA = new TermConcept(cs, "ParentA"); cs.getConcepts().add(parentA); id = myCodeSystemDao.update(codeSystem, null, true, true, mySrd).getId().toUnqualified(); table = myResourceTableDao.findOne(id.getIdPartAsLong()); cs.setResource(table); cs.setResourceVersionId(table.getVersion()); myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, cs); // Try to update to a different resource codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); table = myResourceTableDao.findOne(id.getIdPartAsLong()); cs.setResource(table); cs.setResourceVersionId(table.getVersion()); try { myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, cs); fail(); } catch (UnprocessableEntityException e) { assertThat(e.getMessage(), containsString("Can not create multiple code systems with URI \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/")); } } }